diff --git a/source/org/thdl/quilldriver/QD.java b/source/org/thdl/quilldriver/QD.java index 7693a9a..fd700a6 100644 --- a/source/org/thdl/quilldriver/QD.java +++ b/source/org/thdl/quilldriver/QD.java @@ -44,12 +44,9 @@ import javax.xml.transform.*; import org.thdl.util.ThdlDebug; import org.thdl.util.ThdlActionListener; import org.thdl.util.ThdlAbstractAction; +import org.thdl.util.ThdlOptions; public class QD extends JDesktopPane { - /** When opening a file, this is the only extension QuillDriver - cares about. This is case-insensitive. */ - protected final static String dotQuillDriver = ".xml"; - //project related data protected Project project; @@ -65,7 +62,6 @@ public class QD extends JDesktopPane { protected JInternalFrame actionFrame = null; //miscellaneous stuff - protected JFileChooser fileChooser = null; public JTabbedPane jtp; public TimeCodeManager tcp = null; public SpeakerManager spm = null; @@ -76,7 +72,12 @@ public class QD extends JDesktopPane { public Style componentStyle; public DataFlavor timeFlavor; - protected JMenu editMenu; + protected String thdl_mediaplayer_property = null; + + //class fields because they are affected depending on whether we're + //in read-only or edit mode + protected JMenu editMenu, searchMenu; + protected JMenuItem replaceTextItem; protected ResourceBundle messages; @@ -91,6 +92,10 @@ public class QD extends JDesktopPane { protected URL keyboard_url = null; + protected KeyStroke cutKey, copyKey, pasteKey, selectAllKey; + protected KeyStroke insert1TimeKey, insert2TimesKey, insertSpeakerKey; + protected KeyStroke findKey, replaceKey; + public QD(ResourceBundle messages) { this.messages = messages; @@ -101,9 +106,6 @@ public QD(ResourceBundle messages) { clockIcon = new ImageIcon(QD.class.getResource("clock.gif")); timeFlavor = new DataFlavor(Integer.class, "time"); - fileChooser = new JFileChooser(); - fileChooser.addChoosableFileFilter(new XMLFilter()); - Action insert1TimeAction = new ThdlAbstractAction("insert1Time", null) { public void theRealActionPerformed(ActionEvent e) { new TimePoint(pane, clockIcon, tcp.getOutTime()); @@ -127,22 +129,32 @@ public QD(ResourceBundle messages) { } }; +/* Action saveAction = new ThdlAbstractAction("saveTranscript", null) { public void theRealActionPerformed(ActionEvent e) { getSave(); } }; +*/ JTextPane tp = new JTextPane(); Keymap keymap = tp.addKeymap("QDBindings", tp.getKeymap()); - KeyStroke insert1TimeKey = KeyStroke.getKeyStroke(KeyEvent.VK_OPEN_BRACKET, Event.CTRL_MASK); - KeyStroke insert2TimesKey = KeyStroke.getKeyStroke(KeyEvent.VK_CLOSE_BRACKET, Event.CTRL_MASK); - KeyStroke saveKey = KeyStroke.getKeyStroke(KeyEvent.VK_S, Event.CTRL_MASK); + insert1TimeKey = KeyStroke.getKeyStroke(KeyEvent.VK_OPEN_BRACKET, Event.CTRL_MASK); + insert2TimesKey = KeyStroke.getKeyStroke(KeyEvent.VK_CLOSE_BRACKET, Event.CTRL_MASK); + insertSpeakerKey = KeyStroke.getKeyStroke(KeyEvent.VK_S, Event.ALT_MASK); + findKey = KeyStroke.getKeyStroke(KeyEvent.VK_F, Event.CTRL_MASK); + replaceKey = KeyStroke.getKeyStroke(KeyEvent.VK_R, Event.CTRL_MASK); + cutKey = KeyStroke.getKeyStroke(KeyEvent.VK_X, Event.CTRL_MASK); + pasteKey = KeyStroke.getKeyStroke(KeyEvent.VK_V, Event.CTRL_MASK); + copyKey = KeyStroke.getKeyStroke(KeyEvent.VK_C, Event.CTRL_MASK); + selectAllKey = KeyStroke.getKeyStroke(KeyEvent.VK_A, Event.CTRL_MASK); + +// KeyStroke saveKey = KeyStroke.getKeyStroke(KeyEvent.VK_S, Event.CTRL_MASK); keymap.addActionForKeyStroke(insert1TimeKey, insert1TimeAction); keymap.addActionForKeyStroke(insert2TimesKey, insert2TimesAction); - keymap.addActionForKeyStroke(saveKey, saveAction); +// keymap.addActionForKeyStroke(saveKey, saveAction); Speaker[] speakers = new Speaker[0]; SpeakerData speakerData = new SpeakerData(speakers, keymap); @@ -564,6 +576,34 @@ class Work { } } +public void setMediaPlayerProperty(String property) { + if (property.equals("jmf") || property.equals("qt4j")) + if (!thdl_mediaplayer_property.equals(property)) { + thdl_mediaplayer_property = new String(property); + if (project != null) { + File f = project.getMedia(); + if (f != null) { + project.setMedia(null); + project.setMedia(f); + } + } + } +} +public String getMediaPlayerProperty() { + //if already set, return current media player + if (thdl_mediaplayer_property == null) { + //else get default based on system, user prefs, etc. + String os = System.getProperty("os.name").toLowerCase(); + if (os.indexOf("mac") != -1) //macs default to qt4j + thdl_mediaplayer_property = ThdlOptions.getStringOption("thdl.media.player", "qt4j"); + else if (os.indexOf("windows") != -1) + thdl_mediaplayer_property = ThdlOptions.getStringOption("thdl.media.player", "jmf"); + else //put linux etc. here + thdl_mediaplayer_property = ThdlOptions.getStringOption("thdl.media.player", "jmf"); + } + return thdl_mediaplayer_property; +} + class Project extends JPanel { JTextField titleField, mediaField, transcriptField, nameField, taskField; File transcript = null; @@ -603,7 +643,7 @@ public File getMedia() { public void setMedia(File f) { if (f == null) { media = f; - mediaField.setText(media.getPath()); + mediaField.setText(""); if (player != null) { try { player.cmd_stop(); @@ -623,8 +663,7 @@ public void setMedia(File f) { actionFrame.setLocation(0,0); actionFrame.setSize(new Dimension(actionFrame.getSize().width, QD.this.getSize().height)); } - } - else { + } else { try { URL url = f.toURL(); @@ -632,22 +671,21 @@ public void setMedia(File f) { try { player.cmd_stop(); player.destroy(); + player = null; } catch (SmartMoviePanelException smpe) { smpe.printStackTrace(); ThdlDebug.noteIffyCode(); } } - -//if user properties opt for qt4j instead of jmf, then load qt4j instead - - String os = System.getProperty("os.name").toLowerCase(); try { - if (os.indexOf("windows") != -1) { - player = new SmartJMFPlayer(QD.this, url); - } else if (os.indexOf("mac") != -1) { + if (thdl_mediaplayer_property == null) + thdl_mediaplayer_property = getMediaPlayerProperty(); + if (thdl_mediaplayer_property.equals("qt4j")) { player = new SmartQT4JPlayer(); player.loadMovie(url); + } else if (thdl_mediaplayer_property.equals("jmf")) { + player = new SmartJMFPlayer(QD.this, url); } media = f; mediaField.setText(media.getPath()); @@ -753,24 +791,7 @@ public void changeKeyboard(ActionEvent e) { sharedDP2.setupKeyboard(); } -public boolean getSave() { - if (pane.getDocument().getLength() == 0 && speakerTable.getRowCount() == 0) - return true; //nothing to save - - if (project.getTranscript() == null) { - if (project.getMedia() == null) - fileChooser.setSelectedFile(null); - else { - String path = project.getMedia().getPath(); - path = path.substring(0, path.lastIndexOf('.')) + QD.dotQuillDriver; - fileChooser.setSelectedFile(new File(path)); - } - if (fileChooser.showSaveDialog(QD.this) == JFileChooser.APPROVE_OPTION) { - File f = fileChooser.getSelectedFile(); - project.setTranscript(f); - } else - return false; - } +public boolean saveTranscript() { currentWork.stopWork(); //change keyboard back to wylie for a second @@ -917,28 +938,46 @@ if (keyboard_url != null) { } } -public boolean getOpen() { - getSave(); - fileChooser.setSelectedFile(null); - if (fileChooser.showOpenDialog(QD.this) == JFileChooser.APPROVE_OPTION) { - File f = fileChooser.getSelectedFile(); - project = new Project(); - jtp.addTab(messages.getString("ProjectDetails"), project); - if (getLoad(f)) - return true; +protected void clearProject() { + if (project != null) { + project.setMedia(null); //do this first to properly dispose of media player + jtp.remove(project); + project = null; + } - return false; + + if (tcp != null) { + tcp = null; + jtp.remove(tcp); + } + + SpeakerData sd = (SpeakerData)speakerTable.getModel(); + sd.removeAllSpeakers(); + + jtp.invalidate(); + jtp.validate(); + jtp.repaint(); + + pane.setText(""); } -public boolean getLoad(File t) { +public void newProject(File mediaFile) { + clearProject(); + project = new Project(); + jtp.addTab(messages.getString("ProjectDetails"), project); + project.setMedia(mediaFile); +} + +public boolean loadTranscript(File t) { if (!t.exists()) return false; SAXBuilder builder = new SAXBuilder(); - - try { + clearProject(); + project = new Project(); + //change keyboard back to wylie for a second DuffPane dp = (DuffPane)pane; if (keyboard_url != null) { @@ -1052,7 +1091,7 @@ if (keyboard_url != null) { } project.setTranscript(t); - +jtp.addTab(messages.getString("ProjectDetails"), project); return true; } catch (JDOMException jdome) { jdome.printStackTrace(); @@ -1297,7 +1336,6 @@ class SpeakerData extends AbstractTableModel tp.insertComponent(new JLabel(" ", spIcon[sp.getIconID()], SwingConstants.LEFT)); } }; - KeyStroke insertSpeakerKey = KeyStroke.getKeyStroke(KeyEvent.VK_S, Event.ALT_MASK); keymap.addActionForKeyStroke(insertSpeakerKey, insertSpeakerAction); } public boolean addSpeaker(Speaker speaker) { @@ -1411,6 +1449,7 @@ class SpeakerTable extends JTable tc.setCellRenderer(duffRenderer); tc.setHeaderValue(messages.getString("Name")); } + } public JMenuBar getTextMenuBar() { @@ -1442,15 +1481,17 @@ public JMenuBar getTextMenuBar() { viewMenu.addSeparator(); viewMenu.add(suppressTimesItem); - JMenu searchMenu = new JMenu(messages.getString("Search")); + searchMenu = new JMenu(messages.getString("Search")); JMenuItem findTextItem = new JMenuItem(messages.getString("FindText")); + findTextItem.setAccelerator(findKey); findTextItem.addActionListener(new ThdlActionListener() { public void theRealActionPerformed(ActionEvent e) { findText(); } }); - JMenuItem replaceTextItem = new JMenuItem(messages.getString("ReplaceText")); + replaceTextItem = new JMenuItem(messages.getString("ReplaceText")); + replaceTextItem.setAccelerator(replaceKey); replaceTextItem.addActionListener(new ThdlActionListener() { public void theRealActionPerformed(ActionEvent e) { replaceText(); @@ -1465,14 +1506,28 @@ public JMenuBar getTextMenuBar() { searchMenu.add(findspeakerItem); searchMenu.add(findtimeItem); +/* this is a bit odd. + since all the following keys are already associated with + actions via the keymap, these menu items don't need to actually do anything. + however, i have added the accelerators so that the keyboard shortcuts + are shown next to the menu items. */ + editMenu = new JMenu(messages.getString("Edit")); JMenuItem cutItem = new JMenuItem(messages.getString("Cut")); + cutItem.setAccelerator(cutKey); JMenuItem copyItem = new JMenuItem(messages.getString("Copy")); + copyItem.setAccelerator(copyKey); JMenuItem pasteItem = new JMenuItem(messages.getString("Paste")); + pasteItem.setAccelerator(pasteKey); JMenuItem selectallItem = new JMenuItem(messages.getString("SelectAll")); + selectallItem.setAccelerator(selectAllKey); JMenuItem insertspeakerItem = new JMenuItem(messages.getString("InsertSpeaker")); + insertspeakerItem.setAccelerator(insertSpeakerKey); JMenuItem insertonetimeItem = new JMenuItem(messages.getString("InsertOneTime")); + insertonetimeItem.setAccelerator(insert1TimeKey); JMenuItem inserttwotimesItem = new JMenuItem(messages.getString("InsertTwoTimes")); + inserttwotimesItem.setAccelerator(insert2TimesKey); + editMenu.add(cutItem); editMenu.add(copyItem); editMenu.add(pasteItem); @@ -1675,6 +1730,7 @@ class RadioListener extends ThdlActionListener { //reinstate the edit menu to the pane textFrame.getJMenuBar().add(editMenu); + searchMenu.add(replaceTextItem, 1); textFrame.invalidate(); textFrame.validate(); textFrame.repaint(); @@ -1698,6 +1754,7 @@ class RadioListener extends ThdlActionListener { //remove the edit menu from the pane textFrame.getJMenuBar().remove(editMenu); + searchMenu.remove(replaceTextItem); textFrame.invalidate(); textFrame.validate(); textFrame.repaint(); @@ -1716,20 +1773,4 @@ class RadioListener extends ThdlActionListener { } } } - - private class XMLFilter extends javax.swing.filechooser.FileFilter { - // accepts all directories and all savant files - - public boolean accept(File f) { - if (f.isDirectory()) { - return true; - } - return f.getName().toLowerCase().endsWith(QD.dotQuillDriver); - } - - //the description of this filter - public String getDescription() { - return "Extensible Markup Language (" + QD.dotQuillDriver + ")"; - } - } } diff --git a/source/org/thdl/quilldriver/QDShell.java b/source/org/thdl/quilldriver/QDShell.java index 7a9ff05..11d2c28 100644 --- a/source/org/thdl/quilldriver/QDShell.java +++ b/source/org/thdl/quilldriver/QDShell.java @@ -29,8 +29,16 @@ import javax.swing.text.rtf.*; import org.thdl.util.ThdlDebug; import org.thdl.util.ThdlActionListener; +import org.thdl.util.ThdlOptions; + +import org.thdl.savant.JdkVersionHacks; + public class QDShell extends JFrame { + /** When opening a file, this is the only extension QuillDriver + cares about. This is case-insensitive. */ + protected final static String dotQuillDriver = ".xml"; + ResourceBundle messages = null; QD qd = null; @@ -68,33 +76,32 @@ public class QDShell extends JFrame { public QDShell(Locale locale) { setTitle("QuillDriver"); -/* // Code for Merlin - if (getToolkit().isFrameStateSupported(Frame.MAXIMIZED_BOTH)) - { + if (JdkVersionHacks.maximizedBothSupported(getToolkit())) { setLocation(0,0); setSize(getToolkit().getScreenSize().width,getToolkit().getScreenSize().height); setVisible(true); - setExtendedState(Frame.MAXIMIZED_BOTH); + + // call setExtendedState(Frame.MAXIMIZED_BOTH) if possible: + if (!JdkVersionHacks.maximizeJFrameInBothDirections(this)) { + throw new Error("badness at maximum: the frame state is supported, but setting that state failed. JdkVersionHacks has a bug."); + } } else { -*/ Dimension gs = getToolkit().getScreenSize(); setLocation(0,0); setSize(new Dimension(gs.width, gs.height)); setVisible(true); - -// } + } messages = ResourceBundle.getBundle("MessageBundle", locale); - setJMenuBar(getQDShellMenu()); qd = new QD(messages); getContentPane().add(qd); + setJMenuBar(getQDShellMenu()); addWindowListener(new WindowAdapter () { public void windowClosing (WindowEvent e) { - qd.getSave(); -//should only system exit if no cancel!! - System.exit(0); + if (getSave()) + System.exit(0); } }); } @@ -102,25 +109,49 @@ public class QDShell extends JFrame { public JMenuBar getQDShellMenu() { JMenu projectMenu = new JMenu(messages.getString("Project")); JMenuItem newItem = new JMenuItem(messages.getString("New")); + newItem.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { + if (getSave()) { + JFileChooser fc = new JFileChooser(); + if (fc.showDialog(QDShell.this, messages.getString("SelectMedia")) == JFileChooser.APPROVE_OPTION) { + File mediaFile = fc.getSelectedFile(); + qd.newProject(mediaFile); + } + } + } + }); JMenuItem openItem = new JMenuItem(messages.getString("Open")); openItem.addActionListener(new ThdlActionListener() { public void theRealActionPerformed(ActionEvent e) { - qd.getOpen(); + if (getSave()) { + JFileChooser fc = new JFileChooser(); + fc.addChoosableFileFilter(new QDFileFilter()); + if (fc.showDialog(QDShell.this, messages.getString("OpenTranscript")) == JFileChooser.APPROVE_OPTION) { + File transcriptFile = fc.getSelectedFile(); + qd.loadTranscript(transcriptFile); + } + } } }); JMenuItem closeItem = new JMenuItem(messages.getString("Close")); + closeItem.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { + if (getSave()) + qd.clearProject(); + } + }); + JMenuItem saveItem = new JMenuItem(messages.getString("Save")); saveItem.addActionListener(new ThdlActionListener() { public void theRealActionPerformed(ActionEvent e) { - qd.getSave(); + getSave(); } }); JMenuItem quitItem = new JMenuItem(messages.getString("Quit")); quitItem.addActionListener(new ThdlActionListener() { public void theRealActionPerformed(ActionEvent e) { - qd.getSave(); -//should only system exit if no cancel!! - System.exit(0); + if (getSave()) + System.exit(0); } }); projectMenu.add(newItem); @@ -133,46 +164,117 @@ public class QDShell extends JFrame { //Tibetan-specific value: remove in non-Tibetan version //non-Tibetan specific version would have Transcription Language option here instead - JRadioButton wylieButton = new JRadioButton("THDL Extended Wylie"); - JRadioButton sambhotaButton = new JRadioButton("Sambhota Keymap One"); - JRadioButton tcc1Button = new JRadioButton("TCC Keyboard 1"); - JRadioButton tcc2Button = new JRadioButton("TCC Keyboard 2"); - wylieButton.setActionCommand("wylie"); - sambhotaButton.setActionCommand("sambhota1"); - tcc1Button.setActionCommand("tcc1"); - tcc2Button.setActionCommand("tcc2"); - RadioListener l = new RadioListener(); - wylieButton.addActionListener(l); - sambhotaButton.addActionListener(l); - tcc1Button.addActionListener(l); - tcc2Button.addActionListener(l); - wylieButton.setSelected(true); + JRadioButton[] buttons = new JRadioButton[4]; + buttons[0] = new JRadioButton("THDL Extended Wylie"); + buttons[1] = new JRadioButton("TCC Keyboard 1"); + buttons[2] = new JRadioButton("TCC Keyboard 2"); + buttons[3] = new JRadioButton("Sambhota Keymap One"); + buttons[0].setActionCommand("wylie"); + buttons[1].setActionCommand("tcc1"); + buttons[2].setActionCommand("tcc2"); + buttons[3].setActionCommand("sambhota1"); + ThdlActionListener l1 = new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { + qd.changeKeyboard(e); + } + }; + + buttons[0].addActionListener(l1); + buttons[1].addActionListener(l1); + buttons[2].addActionListener(l1); + buttons[3].addActionListener(l1); ButtonGroup bg = new ButtonGroup(); - bg.add(wylieButton); - bg.add(sambhotaButton); - bg.add(tcc1Button); - bg.add(tcc2Button); - JPanel buttons = new JPanel(new GridLayout(0,1)); - buttons.add(wylieButton); - buttons.add(sambhotaButton); - buttons.add(tcc1Button); - buttons.add(tcc2Button); + bg.add(buttons[0]); + bg.add(buttons[1]); + bg.add(buttons[2]); + bg.add(buttons[3]); + JPanel b1 = new JPanel(new GridLayout(0,1)); + b1.add(buttons[0]); + b1.add(buttons[1]); + b1.add(buttons[2]); + b1.add(buttons[3]); + + int initialKeyboard + = ThdlOptions.getIntegerOption("thdl.default.tibetan.keyboard", 0); + + buttons[initialKeyboard].setSelected(true); + JMenu keyboardMenu = new JMenu(messages.getString("Keyboard")); - keyboardMenu.add(buttons); + keyboardMenu.add(b1); + + + JRadioButton[] buttons2 = new JRadioButton[2]; + buttons2[0] = new JRadioButton("Java Media Framework"); + buttons2[1] = new JRadioButton("Quicktime for Java"); + buttons2[0].setActionCommand("jmf"); + buttons2[1].setActionCommand("qt4j"); + ThdlActionListener l2 = new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { + qd.setMediaPlayerProperty(e.getActionCommand()); + } + }; + + buttons2[0].addActionListener(l2); + buttons2[1].addActionListener(l2); + ButtonGroup bg2 = new ButtonGroup(); + bg2.add(buttons2[0]); + bg2.add(buttons2[1]); + JPanel b2 = new JPanel(new GridLayout(0,1)); + b2.add(buttons2[0]); + b2.add(buttons2[1]); + + String initialMediaPlayer = qd.getMediaPlayerProperty(); + if (initialMediaPlayer.equals("jmf")) + buttons2[0].setSelected(true); + else if (initialMediaPlayer.equals("qt4j")) + buttons2[1].setSelected(true); + + JMenu mediaPlayerMenu = new JMenu(messages.getString("MediaPlayer")); + mediaPlayerMenu.add(b2); JMenu preferencesMenu = new JMenu(messages.getString("Preferences")); preferencesMenu.add(keyboardMenu); + preferencesMenu.add(mediaPlayerMenu); JMenuBar bar = new JMenuBar(); bar.add(projectMenu); bar.add(preferencesMenu); return bar; } + private boolean getSave() { + if (qd.pane.getDocument().getLength() == 0 && qd.speakerTable.getRowCount() == 0) + return true; //nothing to save -class RadioListener extends ThdlActionListener { - public void theRealActionPerformed(ActionEvent e) { - qd.changeKeyboard(e); + JFileChooser fc = new JFileChooser(); + if (qd.project.getTranscript() == null) { + if (qd.project.getMedia() == null) + fc.setSelectedFile(null); + else { + String path = qd.project.getMedia().getPath(); + path = path.substring(0, path.lastIndexOf('.')) + QDShell.dotQuillDriver; + fc.setSelectedFile(new File(path)); + } + if (fc.showSaveDialog(QDShell.this) == JFileChooser.APPROVE_OPTION) { + File f = fc.getSelectedFile(); + qd.project.setTranscript(f); + } else + return false; + } + return qd.saveTranscript(); + } + private class QDFileFilter extends javax.swing.filechooser.FileFilter { + // accepts all directories and all savant files + + public boolean accept(File f) { + if (f.isDirectory()) { + return true; + } + return f.getName().toLowerCase().endsWith(QDShell.dotQuillDriver); + } + + //the description of this filter + public String getDescription() { + return "QD File Format (" + QDShell.dotQuillDriver + ")"; + } } } - -} diff --git a/source/org/thdl/quilldriver/SmartQT4JPlayer.java b/source/org/thdl/quilldriver/SmartQT4JPlayer.java index 0d1eaa2..b284d04 100644 --- a/source/org/thdl/quilldriver/SmartQT4JPlayer.java +++ b/source/org/thdl/quilldriver/SmartQT4JPlayer.java @@ -158,6 +158,7 @@ public class SmartQT4JPlayer extends SmartMoviePanel } public void cmd_playSegment(Integer startTime, Integer stopTime) throws SmartMoviePanelException { +//FIXME: need to deal with null stopTime try { getPlayer().setTime( startTime.intValue() ); @@ -200,11 +201,21 @@ public class SmartQT4JPlayer extends SmartMoviePanel } public int getCurrentTime() { - return 0; + try { + return getMovie().getTime(); + } catch (StdQTException stqte) { + stqte.printStackTrace(); + return -1; + } } public int getEndTime() { - return 0; + try { + return getMovie().getDuration(); + } catch (StdQTException stqte) { + stqte.printStackTrace(); + return -1; + } } //helper methods - QT4J