]> ruin.nu Git - moosique.git/commitdiff
no message
authorEinar Pehrson <einarp@itstud.chalmers.se>
Mon, 26 May 2003 13:21:26 +0000 (13:21 +0000)
committerEinar Pehrson <einarp@itstud.chalmers.se>
Mon, 26 May 2003 13:21:26 +0000 (13:21 +0000)
MooGUI.java
MooInstrumentList.java
MooMenu.java
MooNote.java
MooNoteElement.java
MooToolbar.java
MooTrackTitle.java
MooTrackView.java
Moosique.java
To Do.txt

index 3ecb3081ec6f2aeb9e5c99fc943dca2ca42a00cf..57759c9961699ba3d118e211a213ad0a9627854c 100644 (file)
@@ -19,6 +19,7 @@ public class MooGUI extends JFrame {
        private MooView view;
        private JLabel statusBar;
        private java.util.Timer timer;
+       private boolean updateView = true;
        public static final int statusResetDelay = 3000;
        public static final Font FONT = new Font("Helvetica", Font.PLAIN, 10);
        public static final Color bgColor = new Color(192, 224, 255);
@@ -119,8 +120,10 @@ public class MooGUI extends JFrame {
         * Changes the sequence of the GUI.
         * @param sequence      the MIDI sequence to visualize
         */
-       public void setSequence(Sequence sequence) {
+       public void setSequence(Sequence sequence, File file) {
                seq = sequence;
+               if (file != null) setTitle("Moosique - " + file.getName());
+               else setTitle("Moosique");
                view.setTracks(seq.getTracks(), true);
                toolbar.resetProgInd();
        }
@@ -138,8 +141,8 @@ public class MooGUI extends JFrame {
         * Calls on the main view to update the track views,
         * and on the toolbar to update the progress indicator.
         */
-       public void update(long tickPosition){
-               view.update(tickPosition);
+       public synchronized void update(long tickPosition){
+               if (updateView) view.update(tickPosition);
                toolbar.updateProgInd(tickPosition);
        }
 
@@ -155,6 +158,9 @@ public class MooGUI extends JFrame {
                return octaveAction;
        }
 
+       /** 
+        * Advances the current progress counter by printing a "." to the System output.
+        */
        private void advanceStatus() {
                System.out.print(".");
        }
index 34ff4da07a041ad8ac6e6af8142803c9ce038f9c..91f8eea239733139899db97a5401ed8b585f978b 100644 (file)
@@ -24,7 +24,7 @@ public class MooInstrumentList extends JComboBox implements ActionListener {
        public MooInstrumentList(int chan, int listType, ShortMessage programMsg) {
                super(instruments[listType]);
                programChangeMessage = programMsg;
-               setSelectedIndex(programChangeMessage.getData1());
+               if (programChangeMessage != null) setSelectedIndex(programChangeMessage.getData1());
                setChannel(chan);       
                setFont(Moosique.getGUI().FONT);
                addActionListener(this);
@@ -36,7 +36,7 @@ public class MooInstrumentList extends JComboBox implements ActionListener {
        public void setChannel(int chan) {
                channel = chan;
                try {programChangeMessage.setMessage(programChangeMessage.getCommand(), chan, programChangeMessage.getData1(), 0);}
-               catch (InvalidMidiDataException e) {}
+               catch (Exception e) {}
        }
 
        /**
index 25cab5c99062b20270fbd13bf1f29d4d6724f6ed..a7fe8341e78a6c0208aa7ad52f65699b15c20073 100644 (file)
@@ -1,9 +1,10 @@
 import javax.sound.midi.*;
 import javax.swing.*;
 import javax.swing.filechooser.*;
+import java.awt.*;
 import java.awt.event.*;
 import java.io.*;
-import java.awt.*;
+import java.util.*;
 
 /**
  * Moosiques GUI representing a menubar, menus and menuitems
@@ -12,36 +13,49 @@ import java.awt.*;
  */
 public class MooMenu extends JMenuBar implements ActionListener {
        
-       private JMenu file, edit, keyboard, playback, music, help;
+       private JMenu file, reopen, edit, keyboard, playback, music, help;
        private JFileChooser chooser;
        private File directory;
-       private String[] openedFiles;
+       private ArrayList recentFiles;
 
        /**
         * Creates the menu bar.
         */
        public MooMenu() {
+               // Fetches last work directory, and recent files.
+               // directory = Moosique.getRecentDirectory();
+               ArrayList recentFiles = new ArrayList(); // ... = Moosique.getRecentFiles();
+
+               // Creates the sub-menus and their items.
                file = createMenu("File", KeyEvent.VK_F);
                add(file);
                                
-               addItem(file, "New", KeyEvent.VK_N, ActionEvent.CTRL_MASK);
-               addItem(file, "Open...", KeyEvent.VK_O, ActionEvent.CTRL_MASK);
-               addItem(file, "Save", KeyEvent.VK_S, ActionEvent.CTRL_MASK);
-               addItem(file, "Save as...", KeyEvent.VK_A);
+               addItem(file, "New", KeyEvent.VK_N, true);
+               addItem(file, "Open...", KeyEvent.VK_O, true);
+               reopen = createMenu("Reopen", KeyEvent.VK_R);
+               Iterator it = recentFiles.iterator();
+               while (it.hasNext()) {
+                       JMenuItem recentFile = new JMenuItem((String)it.next());
+                       recentFile.addActionListener(this);
+                       reopen.add(recentFile);
+               }
+               file.add(reopen);
+               addItem(file, "Save", KeyEvent.VK_S, true);
+               addItem(file, "Save as...", KeyEvent.VK_A, false);
                file.addSeparator();
-               addItem(file, "Exit", KeyEvent.VK_Q, ActionEvent.CTRL_MASK);
+               addItem(file, "Exit", KeyEvent.VK_Q, true);
                
                edit = createMenu("Edit", KeyEvent.VK_E);
                add(edit);
                
-               addItem(edit, "Copy", KeyEvent.VK_C, ActionEvent.CTRL_MASK);
-               addItem(edit, "Cut", KeyEvent.VK_X, ActionEvent.CTRL_MASK);
-               addItem(edit, "Paste", KeyEvent.VK_V, ActionEvent.CTRL_MASK);
+               addItem(edit, "Copy", KeyEvent.VK_C, true);
+               addItem(edit, "Cut", KeyEvent.VK_X, true);
+               addItem(edit, "Paste", KeyEvent.VK_V, true);
                edit.addSeparator();
-               addItem(edit, "Select all", KeyEvent.VK_E, ActionEvent.CTRL_MASK);
-               addItem(edit, "Invert selection", KeyEvent.VK_I, ActionEvent.CTRL_MASK);
+               addItem(edit, "Select all", KeyEvent.VK_E, true);
+               addItem(edit, "Invert selection", KeyEvent.VK_I, true);
                edit.addSeparator();
-               addItem(edit, "Preferences...", KeyEvent.VK_P, ActionEvent.CTRL_MASK);
+               addItem(edit, "Preferences...", KeyEvent.VK_P, true);
                
                playback = createMenu("Playback", KeyEvent.VK_P);
                add(playback);
@@ -50,34 +64,34 @@ public class MooMenu extends JMenuBar implements ActionListener {
                addItem(playback, "Pause", "F7", KeyEvent.VK_A);
                addItem(playback, "Stop", "F6", KeyEvent.VK_S);
                playback.addSeparator();
-               addItem(playback, "Set position...", KeyEvent.VK_E);
+               addItem(playback, "Set position...", KeyEvent.VK_E, false);
                playback.addSeparator();
                keyboard = createMenu("Set keyboard octave", KeyEvent.VK_K);
                edit.add(keyboard);
-               for (int i = 9; i >= 0; i--) addItem(keyboard, "Octave " + i, i + 48);
+               for (int i = 9; i >= 0; i--) addItem(keyboard, "Octave " + i, i + 48, false);
                
                music = createMenu("Music", KeyEvent.VK_M);
                add(music);
                
-               addItem(music, "Add track...", KeyEvent.VK_A, ActionEvent.CTRL_MASK);
-               addItem(music, "Delete track...", KeyEvent.VK_D, ActionEvent.CTRL_MASK);
-               addItem(music, "Copy track...", KeyEvent.VK_Y, ActionEvent.CTRL_MASK);
-               addItem(music, "Move track...", KeyEvent.VK_M, ActionEvent.CTRL_MASK);
+               addItem(music, "Add track...", KeyEvent.VK_A, true);
+               addItem(music, "Delete track...", KeyEvent.VK_D, true);
+               addItem(music, "Copy track...", KeyEvent.VK_Y, true);
+               addItem(music, "Move track...", KeyEvent.VK_M, true);
                music.addSeparator();
-               addItem(music, "Insert measure...", KeyEvent.VK_I);
-               addItem(music, "Delete measure...", KeyEvent.VK_E);
+               addItem(music, "Insert measure...", KeyEvent.VK_I, false);
+               addItem(music, "Delete measure...", KeyEvent.VK_E, false);
                music.addSeparator();
-               addItem(music, "Set time signature...", KeyEvent.VK_S);
-               addItem(music, "Set tempo...", KeyEvent.VK_M);
-               addItem(music, "Scale velocity...", KeyEvent.VK_V);
-               addItem(music, "Transpose...", KeyEvent.VK_T);
+               addItem(music, "Set time signature...", KeyEvent.VK_S, false);
+               addItem(music, "Set tempo...", KeyEvent.VK_M, false);
+               addItem(music, "Scale velocity...", KeyEvent.VK_V, false);
+               addItem(music, "Transpose...", KeyEvent.VK_T, false);
                
                help = createMenu("Help", KeyEvent.VK_L);
                add(help);
-               
+
                addItem(help, "User manual", "F1", KeyEvent.VK_M);
                help.addSeparator();
-               addItem(help, "About", KeyEvent.VK_A);
+               addItem(help, "About", KeyEvent.VK_A, false);
        }
        /**
         * Creats a menu in the menubar.
@@ -95,11 +109,15 @@ public class MooMenu extends JMenuBar implements ActionListener {
         * Creats a menuitem in the menu.
         * @param menu          The menu where to add the menuitem.
         * @param name          The name of the menuitem.
+        * @param mnemonic      The keyboard mnemonic used to access this menuitem
+        * @param accelerate    whether to create a keyboard accelerator for this item
         * @return item         The menuitem created.
         */
-       private JMenuItem addItem(JMenu menu, String name, int mnemonic) {
+       private JMenuItem addItem(JMenu menu, String name, int mnemonic, boolean accelerate) {
                JMenuItem item = new JMenuItem(name);
                item.addActionListener(this);
+               item.setMnemonic(mnemonic);
+               if (accelerate) item.setAccelerator(KeyStroke.getKeyStroke(mnemonic, ActionEvent.CTRL_MASK));
                menu.add(item);
                return item;
        }
@@ -109,34 +127,18 @@ public class MooMenu extends JMenuBar implements ActionListener {
         * @param menu          The menu to where to add the menuitem.
         * @param name          The name of the menuitem.
         * @param key           The keystroke to access this menuitem.
+        * @param mnemonic      The keyboard mnemonic used to access this menuitem
         * @return item         The menuitem created.
         */
        private JMenuItem addItem(JMenu menu, String name, String key, int mnemonic) {
                JMenuItem item = new JMenuItem(name);
-               item.setAccelerator(KeyStroke.getKeyStroke(key));
                item.setMnemonic(mnemonic);
+               item.setAccelerator(KeyStroke.getKeyStroke(key));
                item.addActionListener(this);
                menu.add(item);
                return item;
        }
-       
-       /**
-        * Creats a menuitem in the menu.
-        * @param menu          The menu to where to add the menuitem.
-        * @param name          The name of the menuitem.
-        * @param key           The keystroke to access this menuitem.
-        * @param mask          The keyboard mask.
-        * @return item         The menuitem created.
-        */
-       private JMenuItem addItem(JMenu menu, String name, int key, int mask) {
-               JMenuItem item = new JMenuItem(name);
-               item.setAccelerator(KeyStroke.getKeyStroke(key, mask));
-               item.setMnemonic(key);
-               item.addActionListener(this);
-               menu.add(item);
-               return item;
-       }
-       
+
        private boolean isMidiFile(File f) {
                if(f != null) {
                        String extension = f.getName().substring(f.getName().lastIndexOf('.') + 1).toLowerCase().trim();
@@ -168,8 +170,19 @@ public class MooMenu extends JMenuBar implements ActionListener {
                        File file = chooser.getSelectedFile();
                        if(returnVal == JFileChooser.APPROVE_OPTION && isMidiFile(file)) {
                                directory = file.getParentFile();
-                               if (!Moosique.promptOnUnsavedChanges())
-                                       Moosique.load(file);
+                               if (!Moosique.promptOnUnsavedChanges()) {
+                                       if (Moosique.load(file)) {
+                                               // Adds an item for this file to the reopen menu.
+                                               String filename = file.getAbsolutePath();
+                                               JMenuItem recentFile = new JMenuItem(filename);
+                                               for (int i = 0; i < reopen.getMenuComponentCount(); i++) {
+                                                       if (filename.equals(((JMenuItem)reopen.getMenuComponent(i)).getText()))
+                                                               reopen.remove(i);
+                                               }
+                                               reopen.insert(recentFile, 0);
+                                               if (reopen.getMenuComponentCount() > 5) reopen.remove(5);
+                                       }
+                               }
                        }
                } else if (command == "Save") {
                        if (!Moosique.save()) showSaveAsDialog();
@@ -233,6 +246,9 @@ public class MooMenu extends JMenuBar implements ActionListener {
                                "About Moosique",
                                JOptionPane.INFORMATION_MESSAGE,
                                new ImageIcon(Moosique.getGUI().logo));
+               } else if (reopen.isMenuComponent((JMenuItem)e.getSource())) {
+                       String recentFile = ((JMenuItem)e.getSource()).getText();
+                       if (!Moosique.promptOnUnsavedChanges()) Moosique.load(new File(recentFile));
                }
        }
 
@@ -270,6 +286,4 @@ public class MooMenu extends JMenuBar implements ActionListener {
                        return "MIDI files";
                }
        }
-       
-       
 }
index 5628b1df0e6308140fdd39e5e9804f49b6adf065..2a82b6d21b5ac48489aa1a5548f49f96152bb21e 100644 (file)
@@ -1,7 +1,7 @@
 import javax.sound.midi.*;
 
 /**
- * Functional representation of a MIDI note, which adds functionality to the existent MidiEvent class.
+ * Functional representation of a MIDI note, which adds functionality to the existing MidiEvent class.
  * Also provides a reference to the corresponding NoteOff event.
  * 
  * @author  Einar Pehrson
index beed5c416acb498a25a95ff46291e419d3733ddd..42955a8c9e0f6eb9182e61c0c5f2f4f08478c817 100644 (file)
@@ -13,10 +13,6 @@ public class MooNoteElement extends JPanel implements Comparable{
 
        private MooTrackView mtv;
        private MooNote note;
-       private JPopupMenu popup;
-       private JMenu popupTranspUp, popupTranspDown;
-       private JMenuItem popupRemove, popupProp;
-       private JMenuItem[] popupTranspUpItems, popupTranspDownItems;
        private Rectangle pitchRect, veloRect;
        private String notePitch, noteVelocity;
        private boolean selected = false, leftMouseButtonPressed = false, mouseIn = false;
@@ -41,15 +37,6 @@ public class MooNoteElement extends JPanel implements Comparable{
                // Defines coordinates.
                pitchRect = new Rectangle(0, 0, 15, 10);
                veloRect = new Rectangle(20, 0, 40, 10);
-
-               // Creates pop-up menu.
-               popup = new JPopupMenu();
-               popupProp = addMenuItem(popup, "Preferences...");
-               popupRemove = addMenuItem(popup, "Remove");
-               popupTranspUpItems = new JMenuItem[12];
-               popupTranspDownItems = new JMenuItem[12];
-               popupTranspUp = createTransposeMenu(popup, popupTranspUpItems, "note up");
-               popupTranspDown = createTransposeMenu(popup, popupTranspDownItems, "note down");
        }
 
        /** 
@@ -61,7 +48,7 @@ public class MooNoteElement extends JPanel implements Comparable{
        }
 
        /** 
-        * Compares the note of this element to that of another note.
+        * Compares the note of this element to that of another element.
         * @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object
         */
        public int compareTo(Object o) {
@@ -73,7 +60,7 @@ public class MooNoteElement extends JPanel implements Comparable{
         */
        public void select() {
                selected = true;
-               mtv.selectNote(this);
+               Moosique.selectNote(this);
                setBackground(invBgColor);
                textColor = Color.white;
                repaint();
@@ -173,40 +160,6 @@ public class MooNoteElement extends JPanel implements Comparable{
                mtv.layoutElement(this,true);
        }
 
-       /**
-        * 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 trans;
-       }
-
        /**
         * Listener that checks the mouse actions on this element.
         */
@@ -274,9 +227,8 @@ public class MooNoteElement extends JPanel implements Comparable{
                public void mouseReleased(MouseEvent e) {
                        if (!maybeShowPopup(e) && !mouseIn) {
                                int y = e.getY();
-                               if (y < 0) mtv.maybeMoveSelectedNotes((int)Math.floor((double)y / MooTrackView.NOTE_HEIGHT) * MooTrackView.NOTE_HEIGHT);
-                               if (y > getHeight()) mtv.maybeMoveSelectedNotes((int)Math.ceil(((double)y - getHeight()) / MooTrackView.NOTE_HEIGHT) * MooTrackView.NOTE_HEIGHT);
-                               
+                               if (y < 0) mtv.moveSelectedNotes((Moosique.getSequence().getResolution() / 4)* (int)Math.floor((double)y / MooTrackView.NOTE_HEIGHT));
+                               if (y > getHeight()) mtv.moveSelectedNotes((Moosique.getSequence().getResolution() / 4) * (int)Math.ceil(((double)y - getHeight()) / MooTrackView.NOTE_HEIGHT));
                        }
                }
 
@@ -285,38 +237,8 @@ public class MooNoteElement extends JPanel implements Comparable{
                 */
                private boolean maybeShowPopup(MouseEvent e) {
                        if (!e.isPopupTrigger()) return false;
-                       if (!e.isControlDown()) {
-                               if (!selected || mtv.isTheOnlySelected((MooNoteElement)e.getComponent())) popup.show(e.getComponent(), e.getX(), e.getY());
-                               else mtv.showSelectionPopup(e.getComponent(), e.getX(), e.getY());
-                       }
+                       if (!e.isControlDown()) mtv.showSelectionPopup(e.getComponent(), e.getX(), e.getY());
                        return true;
                }
        }
-
-       /**
-        * 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 == popupProp) {
-                               System.out.println("Duration: " + note.getDuration());
-                               new MooDialog(note);
-                               System.out.println("Duration: " + note.getDuration());
-                               newLayout();
-                               update();
-                       } else if (source == popupRemove) {
-                               remove();
-                       } else if (source == popupTranspUpItems[0]) {
-                               transpose(12);
-                       } else if (source == popupTranspDownItems[0]) {
-                               transpose(-12);
-                       } else {
-                               for (int i = 1; i < 12; i++) {
-                                       if (source == popupTranspUpItems[i]) transpose(i);
-                                       else if (source == popupTranspDownItems[i]) transpose(-i);
-                               }
-                       }
-               }
-       }
 }
index 5be8fdb77a29d0ddfd251bce0fd60433c9c79333..bca50efba950d727f80be24e97136d40795fcb17 100644 (file)
@@ -1,7 +1,7 @@
+import javax.sound.midi.*;
 import javax.swing.*;
 import java.awt.*;
 import java.awt.event.*;
-import javax.sound.midi.*;
 
 /**
  * The application's toolbar, with the most frequently used commands.
@@ -75,17 +75,16 @@ public class MooToolbar extends JToolBar {
         * @param tickPosition  the tick position to visualize
         */
        public void updateProgInd(long tickPosition) {
-               ticksPerBeat = Moosique.getSequence().getResolution();
+               System.out.print("Updating to " + tickPosition + " = ");
                if (tickPosition == 0) {
                        resetProgInd();
+                       System.out.println("1:1:1");
                } else {
-                       // Otherwise, calculates the current song position in measures, beats and ticks.
-                       long measures = tickPosition / (beatsPerMeasure * ticksPerBeat);
-                       long beats = (tickPosition - measures * beatsPerMeasure * ticksPerBeat) / ticksPerBeat;
-                       long ticks = tickPosition - measures * beatsPerMeasure * ticksPerBeat - beats * ticksPerBeat;
-                       measuresValue.setText(Long.toString(1 + measures));
-                       beatsValue.setText(Long.toString(1 + beats));
-                       ticksValue.setText(Long.toString(1 + ticks));
+                       int[] position = Moosique.getPositionForTicks(tickPosition);
+                       System.out.println("" + position[0] + ":" + position[1] + ":" + position[2]);
+                       measuresValue.setText(Integer.toString(position[0]));
+                       beatsValue.setText(Long.toString(position[1]));
+                       ticksValue.setText(Long.toString(position[2]));
                }
        }
 
index 6a3e1b5f8cb638d331aedb00a3d4b7b726ef1bfe..4822dca5ced2a90e409c49feebb5c008747b5594 100644 (file)
@@ -131,11 +131,13 @@ public class MooTrackTitle extends JPanel {
        }
 
        /**
-        * Checks if the focus is lost.
+        * When the title field loses focus, updates the corresponding MidiEvent.
         */
        class TitleFocusListener extends FocusAdapter {
                public void focusLost(FocusEvent e) {
-                       // Update the MidiEvent containing the title of this track
+                       try {
+                               trackNameMessage.setMessage(3, title.getText().getBytes(), title.getText().length());
+                       } catch (InvalidMidiDataException ex) {}
                }
        }
 
@@ -150,7 +152,7 @@ public class MooTrackTitle extends JPanel {
                                MidiEvent me;
                                MooNote mn;
                                instruments.setChannel(channel);
-                               // Query the user before rechannelling???
+                               // Prompt the user before rechannelling???
                                for (int j = 0; j < track.size(); j++) {
                                        me = track.get(j);
                                        if (me instanceof MooNote){
index 916e8fc387335f9cf1dac22d516a3606665696e4..1c7abe4a979bab0b6294131b4b556f132ef3508e 100644 (file)
@@ -18,20 +18,20 @@ public class MooTrackView extends JPanel {
        private MooKeyboard keyboard;
 
        private JPopupMenu popup, selPopup;
-       private JMenu selPopupTranspUp, selPopupTranspDown;
-       private JMenuItem popupAdd, popupPaste;
+       private JMenu popupAdd, selPopupTranspUp, selPopupTranspDown;
+       private JMenuItem popupAddItemsCustom, popupAddItemsLast, popupPaste;
+       private JMenuItem popupAddItemsWhole, popupAddItemsHalf, popupAddItemsQuarter, popupAddItemsEighth, popupAddItemsSixteenth;
        private JMenuItem selPopupCopy, selPopupCut, selPopupRemove;
        private JMenuItem[] selPopupTranspUpItems, selPopupTranspDownItems;
 
        private ArrayList coords; 
-       private TreeSet selection;
        private Insets insets;
        private Rectangle box;
-       private int ticksPerSixteenth, popupY = 0;
+       private int ticksPerSixteenth, popupY = 0, lastNoteLength = 2;
        private boolean leftMouseButtonPressed = false;
        private static boolean snapToSixteenths = true;
-       protected static int viewLength = 0;
-       protected static int extraHeight = 0;
+       private static int viewLength = 0;
+       private static int extraHeight = 0;
        public static final int NOTE_HEIGHT = 10, NOTE_WIDTH = 40, VIEW_WIDTH = 200;
 
        /**
@@ -48,7 +48,6 @@ public class MooTrackView extends JPanel {
                ticksPerSixteenth = Moosique.getSequence().getResolution() / 4;
                insets = getInsets();
                coords = new ArrayList(track.size() / 2);
-               selection = new TreeSet();
 
                // Configures panel
                setBackground(Color.white);
@@ -76,7 +75,16 @@ public class MooTrackView extends JPanel {
 
                // Creates panel pop-up menu.
                popup = new JPopupMenu();
-               popupAdd = addMenuItem(popup, "Add note...");
+               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.
@@ -209,10 +217,11 @@ public class MooTrackView extends JPanel {
        /**
         * Adds a standard note to this track.
         */
-       private void addStandardNote() {
+       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));
        }
 
        /** 
@@ -230,49 +239,11 @@ public class MooTrackView extends JPanel {
                repaint();
        }
 
-       /**
-        * Selects the given note
-        * @param the note to select
-        */
-       public void selectNote(MooNoteElement elem) {
-               selection.add(elem);
-       }
-
-       /**
-        * Deselects the given note
-        * @param the note to deselect
-        */
-       public void deselectNote(MooNoteElement elem) {
-               selection.remove(elem);
-       }
-
-       /**
-        * Deselects all notes.
-        */
-       public void deselectAllNotes() {
-               Iterator it = selection.iterator();
-               while(it.hasNext()) {
-                       ((MooNoteElement)it.next()).deselect();
-               }
-               selection.clear();
-       }
-
-       /**
-        * 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
-        */
-       public boolean isTheOnlySelected(MooNoteElement elem) {
-               Iterator it = selection.iterator();
-               while(it.hasNext()) {
-                       if (!it.next().equals(elem)) return false;
-               }
-               return true;
-       }
-
        /**
         * Copies the current selection.
         */
        public void copySelectedNotes() {
+               TreeSet selection = Moosique.getSelection();
                ArrayList copyBuffer = new ArrayList(selection.size());
                Iterator it = selection.iterator();
                while(it.hasNext()) {
@@ -306,6 +277,7 @@ public class MooTrackView extends JPanel {
                                mn.setChannel(title.getChannel());
                                addNote(mn);
                        }
+                       Moosique.setEdited();
                }
        }
 
@@ -313,22 +285,26 @@ public class MooTrackView extends JPanel {
         * Removes the current selection.
         */
        public void removeSelectedNotes() {
+               TreeSet selection = Moosique.getSelection();
                Iterator it = selection.iterator();
                while(it.hasNext()) {
                        removeNote((MooNoteElement)it.next());
                }
                selection.clear();
+               Moosique.setEdited();
        }
 
        /**
         * Transposes all selected notes the given number of halftones.
         */
        private void transposeSelectedNotes(int halftones) {
+               TreeSet selection = Moosique.getSelection();
                Iterator it = selection.iterator();
                while(it.hasNext()) {
                        MooNoteElement elem = (MooNoteElement)it.next();
                        elem.transpose(halftones);
                }
+               Moosique.setEdited();
        }
 
        /**
@@ -336,6 +312,7 @@ public class MooTrackView extends JPanel {
         * @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();
@@ -354,14 +331,7 @@ public class MooTrackView extends JPanel {
                                layoutElement(elem, true);
                        }
                }
-       }
-
-       /**
-        * Moves the current selection, depending on the given delta y.
-        * @param y     the number of pixels the selection is moved.
-        */
-       public void maybeMoveSelectedNotes(int y) {
-               moveSelectedNotes(ticksPerSixteenth * (y / NOTE_HEIGHT));
+               Moosique.setEdited();
        }
 
        /**
@@ -456,10 +426,10 @@ 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);
                                }
                        }
                }
@@ -500,8 +470,23 @@ public class MooTrackView extends JPanel {
                public void actionPerformed(ActionEvent e) {
                        Object source = e.getSource();
                        // Handling panel popup actions.
-                       if (source == popupAdd) {
-                               addStandardNote();
+                       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.
index e2b7f3e490821832347b01fae8c9289f9eff228a..5ad1afe9d93b8ae55a9201e6c1464eed52dfd0a5 100644 (file)
@@ -22,6 +22,7 @@ public class Moosique {
        private static MidiChannel activeChannel;
 
        private static ArrayList copyBuffer, emptyTracks, timeSignatures, tempoChanges;
+       private static TreeSet selection;
        private static Map trackMute = new HashMap();
        private static Map trackSolo = new HashMap();
        private static Thread player;
@@ -39,7 +40,7 @@ public class Moosique {
         * loads a sequence and creates the GUI.
         */
        public static void main (String[] args) {
-               out("\nMoosique version 1.0\n");
+               out("\nMoosique version 1.0\n", true);
 
                // Parses command-line arguments.
                String fileArg = null;
@@ -49,17 +50,17 @@ public class Moosique {
                }
 
                // Acquires MIDI devices and connects them.
-               System.out.print("Initializing MIDI devices.");
+               out("Initializing MIDI devices.", false);
                try {
                        // Configures sequencer
                        sequencer = MidiSystem.getSequencer();
-                       System.out.print(".");
+                       advanceStatus();
                        sequencer.open();
                        sequencer.addMetaEventListener(new SongEndListener());
 
                        // Configures synthesizer
                        synthesizer = MidiSystem.getSynthesizer();
-                       System.out.print(".");
+                       advanceStatus();
                        synthesizer.open();
 
                        // Configures receiver, transmitter and channels.
@@ -68,22 +69,22 @@ public class Moosique {
                        channels = synthesizer.getChannels();
                        setActiveChannel(0);
                } catch (MidiUnavailableException e) {
-                       out("Failed, quitting.");
+                       out("Failed, quitting.", true);
                        System.exit(1);
                }
-               out("Done");
+               out("Done", true);
 
                // Loads user preferences (work directory, last opened files etc).
                loadPreferences();
 
                //If a filename is given as the command-line argument, attempts to load a sequence from the file.
                if (fileArg != null) {
-                       System.out.print("Loading MIDI sequence from " + fileArg + "...");
+                       out("Loading MIDI sequence from " + fileArg + "...", false);
                        if (!load(new File(fileArg))) {
-                               out("Failed, creating new sequence");
+                               out("Failed, creating new sequence", true);
                                clearSequence();
                        } else {
-                               out("Done");
+                               out("Done", true);
                        }
                } else {
                        // Otherwise creates a new empty one.
@@ -92,17 +93,17 @@ public class Moosique {
 
                // Builds GUI, unless n-flag is set.
                if (makeGUI) {
-                       System.out.print("Building GUI.");
+                       out("Building GUI.", false);
                        try {
                                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                        } catch (Exception e) {}
                        gui = new MooGUI(seq, file);
-                       out("Done");
+                       out("Done", true);
                } else {
-                       System.out.print("Playing...");
+                       out("Playing...", false);
                        play();
                        while (sequencer.isRunning()) {}
-                       out("Done");
+                       out("Done", true);
                        quit();
                }
        }
@@ -167,17 +168,36 @@ public class Moosique {
 
        /** 
         * Calculates the position (measures, beats, ticks) in the current sequence for the given tick position.
+        * @param tickPosition the tick position for which to calculate the position
         * @return an array of integers where index 0 is measures, 1 is beats and 2 is ticks.
         */
-       public static int[] getPositionForTicks(long ticks) {
-               /*
-               int measures, beats, ticks;
-               for (int i = 0; i < timeSignatures.length; i++) {
-                       long tick = timeSignatures[i].getTick();
-                       // Split the ticks in the interval into measures, beats and ticks.
+       public static int[] getPositionForTicks(long tickPosition) {
+               int ticksPerBeat = seq.getResolution(), sigs = timeSignatures.size(), beatsPerMeasure = 4;
+               long measures = 0, beats = 0, ticks = 0;
+               if (sigs > 1) {
+                       /*
+                       Iterator it = timeSignatures.iterator();
+                       MidiEvent lastTSEvent = (MidiEvent)it.next();
+                       if (lastTSEvent.getTick() != 0) tickPos += (int)lastTSEvent.getTick();
+                       while(it.hasNext()) {
+                               MidiEvent nextTSEvent = (MidiEvent)it.next();
+                               long tickDiff = nextTSEvent.getTick() - lastTSEvent.getTick();
+                               ts = decodeTimeSig(((MetaMessage)lastTSEvent.getMessage()).getData());
+                               beatsPerMeasure = ts[0] * (4 / ts[1]);
+                               tickPos += ((beatsPerMeasure * measures + beats) * res + ticks);
+                       }
+                       */
+               } else {
+                       if (sigs == 1) {
+                               MidiEvent TSEvent = (MidiEvent)timeSignatures.get(0);
+                               int[] ts = decodeTimeSig(((MetaMessage)TSEvent.getMessage()).getData());
+                               beatsPerMeasure = ts[0] * (4 / ts[1]);
+                       }
+                       measures = tickPosition / (beatsPerMeasure * ticksPerBeat);
+                       beats = (tickPosition - measures * beatsPerMeasure * ticksPerBeat) / ticksPerBeat;
+                       ticks = tickPosition - measures * beatsPerMeasure * ticksPerBeat - beats * ticksPerBeat;
                }
-               */
-               int[] pos = {1, 1, 1};
+               int[] pos = {(int)measures + 1, (int)beats + 1, (int)ticks + 1};
                return pos;
        }
 
@@ -228,13 +248,28 @@ public class Moosique {
         * @return the tick position.
         */
        public static long getTicksForPosition(int measures, int beats, int ticks) {
+               int res = seq.getResolution();
                long tickPos = 0;
-               /*
-               for (int i = 0; i < timeSignatures.length; i++) {
-                       long tick = timeSignatures[i].getTick();
-                       // Add the measures, beats and ticks in the interval.
+               switch (timeSignatures.size()) {
+                       case 0:
+                               tickPos = (4 * measures + beats) * res + ticks;
+                       case 1:
+                               MidiEvent TSEvent = (MidiEvent)timeSignatures.get(0);
+                               int[] ts = decodeTimeSig(((MetaMessage)TSEvent.getMessage()).getData());
+                               int beatsPerMeasure = ts[0] * (4 / ts[1]);
+                               tickPos = (beatsPerMeasure * measures + beats) * res + ticks;
+                       default:
+                               Iterator it = timeSignatures.iterator();
+                               MidiEvent lastTSEvent = (MidiEvent)it.next();
+                               if (lastTSEvent.getTick() != 0) tickPos += (int)lastTSEvent.getTick();
+                               while(it.hasNext()) {
+                                       MidiEvent nextTSEvent = (MidiEvent)it.next();
+                                       long tickDiff = nextTSEvent.getTick() - lastTSEvent.getTick();
+                                       ts = decodeTimeSig(((MetaMessage)lastTSEvent.getMessage()).getData());
+                                       beatsPerMeasure = ts[0] * (4 / ts[1]);
+                                       tickPos += ((beatsPerMeasure * measures + beats) * res + ticks);
+                               }
                }
-               */
                return tickPos;
        }
 
@@ -427,7 +462,7 @@ public class Moosique {
         * @return the tempo in beats per minute
         */
        public static int decodeTempo(byte[] bytes) {
-               return 60000000 / (bytes[0] * 65536 + bytes[1] * 256 + bytes[2]); // bytes[0] & 0xFF ???
+               return 60000000 / (bytes[0] * 65536 + bytes[1] * 256 + bytes[2]);
        }
 
        /** 
@@ -439,7 +474,7 @@ public class Moosique {
        public static byte[] encodeTimeSig(int numerator, int denominator) {
                byte[] b = {
                        (byte)numerator,
-                       (byte)(Math.log(denominator) / Math.log(2)),
+                       (byte)(Math.log(denominator) / Math.log(2)), // logarithm of denominator in base 2
                        (byte)96,
                        (byte)8
                };
@@ -465,6 +500,71 @@ public class Moosique {
 
 
 
+       /*                              ***
+        **       SELECTION METHODS      **
+        ***                              */
+
+
+
+
+
+
+
+
+       /** 
+        * Returns the current selection.
+        * @return the current selection
+        */
+       public static TreeSet getSelection() {
+               return selection;
+       }
+
+       /**
+        * Selects the given note
+        * @param the note to select
+        */
+       public static void selectNote(MooNoteElement elem) {
+               selection.add(elem);
+       }
+
+       /**
+        * Deselects the given note
+        * @param the note to deselect
+        */
+       public static void deselectNote(MooNoteElement elem) {
+               selection.remove(elem);
+       }
+
+       /**
+        * Deselects all notes.
+        */
+       public static void deselectAllNotes() {
+               Iterator it = selection.iterator();
+               while(it.hasNext()) {
+                       ((MooNoteElement)it.next()).deselect();
+               }
+               selection.clear();
+       }
+
+       /**
+        * 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
+        */
+       public static boolean isTheOnlySelected(MooNoteElement elem) {
+               Iterator it = selection.iterator();
+               while(it.hasNext()) {
+                       if (!it.next().equals(elem)) return false;
+               }
+               return true;
+       }
+
+
+
+
+
+
+
+
        /*                              ***
         **       PLAYBACK METHODS       **
         ***                              */
@@ -524,7 +624,7 @@ public class Moosique {
                if (sequencer.isRunning()) {
                        sequencer.stop();
                }
-               if (player != null) player.interrupt();
+               player = null;
        }
 
        /** 
@@ -535,7 +635,7 @@ public class Moosique {
                        sequencer.stop();
                }
                sequencer.setTickPosition(editPosition);
-               if (player != null) player.interrupt();
+               player = null;
                gui.update((long)0);
        }
 
@@ -566,15 +666,13 @@ public class Moosique {
                        seq = new Sequence(Sequence.PPQ, DEFAULT_RESOLUTION, DEFAULT_TRACKS);
                        Track[] tracks = seq.getTracks();
 
-                       // Creates messages for default tempo (120) and time signature (4/4).
+                       // Creates messages for default tempo (120) and time signature (4/4), and adds them to track 0.
                        MetaMessage timeSigMsg = new MetaMessage();
                        MetaMessage tempoMsg = new MetaMessage();
                        try {
                                timeSigMsg.setMessage(88, encodeTimeSig(4, 4), 4);
                                tempoMsg.setMessage(81, encodeTempo(120), 3);
                        } catch (InvalidMidiDataException e) {}
-
-                       // Adds them to track 0.
                        tracks[0].add(new MidiEvent(timeSigMsg, (long)0));
                        tracks[0].add(new MidiEvent(tempoMsg, (long)0));
 
@@ -582,17 +680,14 @@ public class Moosique {
                        initializeTrack(tracks[1], 0, 24, "Guitar");
                        initializeTrack(tracks[2], 1, 33, "Bass");
                        initializeTrack(tracks[3], 9, 0, "Drums");
-
-                       file = null;
-                       emptyTracks = new ArrayList();
-                       timeSignatures = new ArrayList();
-                       tempoChanges = new ArrayList();
-                       trackSolo = new HashMap();
-                       trackMute = new HashMap();
-                       copyBuffer = new ArrayList();
                } catch (InvalidMidiDataException e) {}
+
+               // Reinitializes sequence variables
+               file = null;
+               reinitializeVariables();
+
                // Sends the sequence to the GUI.
-               if (gui != null) gui.setSequence(seq);
+               if (gui != null) gui.setSequence(seq, null);
        }
 
        /** 
@@ -609,6 +704,9 @@ public class Moosique {
                                titleMsg.setMessage(3, title.getBytes(), title.length());
                        } catch (InvalidMidiDataException e) {}
 
+                       // Sends the program change to the channel
+                       getChannel(channel).programChange(program);
+
                        // Adds them to the track.
                        track.add(new MidiEvent(programMsg, (long)0));
                        track.add(new MidiEvent(titleMsg, (long)0));
@@ -691,17 +789,11 @@ public class Moosique {
                edited = false;
 
                Track[] tracks = seq.getTracks();
-               emptyTracks = new ArrayList();
-               timeSignatures = new ArrayList();
-               tempoChanges = new ArrayList();
-               trackMute = new HashMap();
-               trackSolo = new HashMap();
-               copyBuffer = new ArrayList();
+               reinitializeVariables();
 
                // Searches track 0 for changes in tempo and time signature.
                MidiEvent event;
                MetaMessage metaMsg;
-               ArrayList ts = new ArrayList(), tc = new ArrayList();
                for (int i = 0; i < tracks[0].size(); i++) {
                        event = tracks[0].get(i);
                        if (event.getMessage().getStatus() == MetaMessage.META) {
@@ -712,6 +804,9 @@ public class Moosique {
                                }
                        }
                }
+               Comparator c = new MidiEventComparator();
+               Collections.sort(tempoChanges, c);
+               Collections.sort(timeSignatures, c);
 
                // Converts tracks.
                for (int i = 0; i < tracks.length; i++) {
@@ -719,7 +814,7 @@ public class Moosique {
                }
 
                // Sends sequence to GUI and sequencer, then returns
-               if (gui != null) gui.setSequence(seq);
+               if (gui != null) gui.setSequence(seq, file);
                try {
                        sequencer.setSequence(seq);
                } catch (InvalidMidiDataException e) {}
@@ -737,6 +832,19 @@ public class Moosique {
                // Math.round(mn.getTick() / ticksPerSixteenth);
        }
 
+       /** 
+        * Reinitializes sequence-specific variables.
+        */
+       private static void reinitializeVariables() {
+               emptyTracks = new ArrayList();
+               timeSignatures = new ArrayList();
+               tempoChanges = new ArrayList();
+               trackSolo = new HashMap();
+               trackMute = new HashMap();
+               copyBuffer = new ArrayList();
+               selection = new TreeSet();
+       }
+
        /** 
         * Loads the user preferences.
         */
@@ -752,7 +860,7 @@ public class Moosique {
        }
 
        /** 
-        * Prompts the user .
+        * Prompts the user to save any unsaved changes.
         */
        public static boolean promptOnUnsavedChanges() {
                if (!edited) return false;
@@ -796,8 +904,16 @@ public class Moosique {
        /** 
         * Prints the given string to the System output.
         */
-       private static void out(String text) {
-               System.out.println(text);
+       private static void out(String text, boolean newLine) {
+               if (newLine) System.out.println(text);
+               else System.out.print(text);
+       }
+
+       /** 
+        * Advances the current progress counter by printing a "." to the System output.
+        */
+       private static void advanceStatus() {
+               out(".", false);
        }
 
        /** 
@@ -829,28 +945,25 @@ public class Moosique {
         */
        public static class PlayThread extends Thread {
                public void run() {
-                       // Updates the GUI with the current tick position.
-                       gui.update(sequencer.getTickPosition());
-
-                       // Puts the thread to sleep for as long as it takes
-                       // the sequencer to reach the next sixteenth.
-                       try {
-                               //sleep((long)((15000 / getTempo()) * (tickDiff / ticksPerSixteenth)));
-                               sleep (10);
-                       } catch (InterruptedException e) {
-                               Moosique.stop();
+                       Thread currentThread = Thread.currentThread();
+                       while(currentThread == player) {
+                               gui.update(sequencer.getTickPosition());
+                               try {
+                                       sleep(10);
+                               } catch (InterruptedException e) {
+                                       Moosique.stop();
+                               }
                        }
                }
        }
 
        /** 
-        * A listener for detecting the end of the sequence.
+        * A listener for detecting the Meta event signifying the end of the sequence.
         */
        public static class SongEndListener implements MetaEventListener {
                public void meta(MetaMessage event) {
                        if (event.getType() == 47)
-                               // End of sequence
                                stop();
-               }
+               }
        }
 }
index f6b6d378584539955b5129035525f6bcf337f4f1..d655e6d1bde2c82d7463820a8d9863a33457f9d2 100644 (file)
--- a/To Do.txt
+++ b/To Do.txt
@@ -11,6 +11,7 @@ Rolle Spara konfiguration
 
 \f
 BUGGAR
+x Exceptions vid inspelning till ny fil.
 x Play-knappen ändras inte till Pause.
 x Varför ritar den ut de tomma spåren i en ny fil? Rätt, men hur?
 x Varför hänger sig Play om man ändrar duration på en not?
@@ -27,8 +28,10 @@ x Textf
 \f
 PREFERENCES
 
-x MIDI Devices - Comboboxar över tillgängliga sequencers och synthesizers.
-x "Allow smaller note elements than 1/16" - Checkbox mot variabeln MooTrackView.snapToSixteenths
+x MIDI Devices                                 Comboboxar över tillgängliga sequencers och synthesizers.
+x "Follow song on play"                        Checkbox mot booleanen MooGUI.updateView
+x "Allow smaller note elements than 1/16"      Checkbox mot booleanen MooTrackView.snapToSixteenths
+
 Vilka mer booleaner ska man kunna ändra?
 
 \f
@@ -40,12 +43,12 @@ SWING
                Mnemonicsarna är konstiga.
 
        MooToolbar
+               Lägg till:
+                       ! Lablar för tempo och taktart
+                       ! Fler knappar - Preferences, Octave up/down, 
+                       ? Volymkontroll Använd Moosique.setVolume(long volume);
+                       ? Progress Bar  getTickLength() för max, getTickPosition() för position.
                Fixa så att allt sitter fast! Med BoxLayout?
-               Lägg till?
-                       Volymkontroll   Använd Moosique.setVolume(long volume);
-                       Progress Bar    getTickLength() för max, getTickPosition() för position.
-                       Lablar för tempo och taktart
-                       Fler knappar - Preferences, Octave up/down, 
 
        MooTrackView
                Ändra längden på MooNoteElement (JPanel).