diff --git a/source/org/thdl/quilldriver/QDConfiguration.java b/source/org/thdl/quilldriver/QDConfiguration.java new file mode 100644 index 0000000..05adc13 --- /dev/null +++ b/source/org/thdl/quilldriver/QDConfiguration.java @@ -0,0 +1,24 @@ + +/* +The contents of this file are subject to the THDL Open Community License +Version 1.0 (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License on the THDL web site +(http://www.thdl.org/). + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific terms governing rights and limitations under the +License. + +The Initial Developer of this software is the Tibetan and Himalayan Digital +Library (THDL). Portions created by the THDL are Copyright 2001 THDL. +All Rights Reserved. + +Contributor(s): ______________________________________. +*/ + +package org.thdl.quilldriver; + +public class QDConfiguration { + +} diff --git a/source/org/thdl/quilldriver/QDShell.java b/source/org/thdl/quilldriver/QDShell.java index 91c09ff..ac7bca8 100644 --- a/source/org/thdl/quilldriver/QDShell.java +++ b/source/org/thdl/quilldriver/QDShell.java @@ -40,18 +40,21 @@ import org.thdl.savant.JdkVersionHacks; public class QDShell extends JFrame { - + protected static final String defaultConfiguration = new String("http://iris.lib.virginia.edu/tibet/education/tllr/xml/lacito-thdl_qdConfig.xml"); + /** the middleman that keeps code regarding Tibetan keyboards * clean */ + /* private final static JskadKeyboardManager keybdMgr = new JskadKeyboardManager(JskadKeyboardFactory.getAllAvailableJskadKeyboards()); - +*/ /** 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; + QDConfiguration qdConfig; public static void main(String[] args) { try { @@ -59,10 +62,12 @@ public class QDShell extends JFrame { Locale locale; - if (args.length == 2) { - locale = new Locale(new String(args[0]), new String(args[1])); +/* + if (args.length == 3) { + locale = new Locale(new String(args[1]), new String(args[2])); ThdlI18n.setLocale(locale); } +*/ try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); @@ -70,14 +75,26 @@ public class QDShell extends JFrame { catch (Exception e) { } - QDShell qdsh = new QDShell(); + QDShell qdsh = new QDShell(args); qdsh.setVisible(true); } catch (NoClassDefFoundError err) { ThdlDebug.handleClasspathError("QuillDriver's CLASSPATH", err); } } - public QDShell() { + public QDShell(String[] args) { + String configURL = null; + String newURL = null; + String editURL = null; + String dtdURL = null; + + switch (args.length) { + case 4: dtdURL = new String(args[3]); + case 3: newURL = new String(args[2]); + case 2: editURL = new String(args[1]); + case 1: configURL = new String(args[0]); + } + setTitle("QuillDriver"); messages = ThdlI18n.getResourceBundle(); @@ -98,42 +115,51 @@ public class QDShell extends JFrame { setVisible(true); } - qd = new QD(); - getContentPane().add(qd); - setJMenuBar(getQDShellMenu()); + if (args.length == 4) { + qd = new QD(configURL, editURL, newURL, dtdURL); + getContentPane().add(qd); + setJMenuBar(getQDShellMenu()); + } else { + try { + String home = System.getProperty("user.home"); + String sep = System.getProperty("file.separator"); + String path = "file:" + home + sep + "put-in-home-directory" + sep; + qd = new QD(path+"config.xml", path+"edit.xsl", path+"new.xsl", path+"dtd.dtd"); + getContentPane().add(qd); + setJMenuBar(getQDShellMenu()); + } catch (SecurityException se) { + se.printStackTrace(); + } + } setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter () { public void windowClosing (WindowEvent e) { - if (getWantSave()) - getSave(); + qd.saveTranscript(); System.exit(0); } }); } public JMenuBar getQDShellMenu() { - JMenu projectMenu = new JMenu(messages.getString("Project")); + JMenu projectMenu = new JMenu(messages.getString("File")); JMenuItem newItem = new JMenuItem(messages.getString("New")); newItem.addActionListener(new ThdlActionListener() { public void theRealActionPerformed(ActionEvent e) { - if (getWantSave()) - getSave(); - qd.clearProject(); - } - }); - - JMenuItem openVideoItem = new JMenuItem(messages.getString("OpenMediaFile")); - openVideoItem.addActionListener(new ThdlActionListener() { - public void theRealActionPerformed(ActionEvent e) { - if (getWantSave()) - getSave(); - + qd.saveTranscript(); + String s = "To start a new annotation, first open a video, " + + "and then create and save an empty annotation file."; JFileChooser fc = new JFileChooser(); if (fc.showDialog(QDShell.this, messages.getString("SelectMedia")) == JFileChooser.APPROVE_OPTION) { File mediaFile = fc.getSelectedFile(); try { - qd.newProject(mediaFile.toURL()); + JFileChooser fc2 = new JFileChooser(); + fc2.addChoosableFileFilter(new QDFileFilter()); + if (fc2.showDialog(QDShell.this, messages.getString("SaveTranscript")) == JFileChooser.APPROVE_OPTION) { + File transcriptFile = fc2.getSelectedFile(); + String mediaString = mediaFile.toURL().toString(); + qd.newTranscript(transcriptFile, mediaString); + } } catch (MalformedURLException murle) { murle.printStackTrace(); ThdlDebug.noteIffyCode(); @@ -142,31 +168,10 @@ public class QDShell extends JFrame { } }); - JMenuItem openURLItem = new JMenuItem(messages.getString("OpenMediaURL")); - openURLItem.addActionListener(new ThdlActionListener() { - public void theRealActionPerformed(ActionEvent e) { - String url = JOptionPane.showInputDialog(QDShell.this, - messages.getString("TypeMediaURL")); - if (url != null) { - if (getWantSave()) - getSave(); - - try { - qd.newProject(new URL(url)); - } catch (MalformedURLException murle) { - murle.printStackTrace(); - ThdlDebug.noteIffyCode(); - } - } - } - }); - - JMenuItem openItem = new JMenuItem(messages.getString("OpenTranscript")); + JMenuItem openItem = new JMenuItem(messages.getString("Open")); openItem.addActionListener(new ThdlActionListener() { public void theRealActionPerformed(ActionEvent e) { - if (getWantSave()) - getSave(); - + qd.saveTranscript(); JFileChooser fc = new JFileChooser(); fc.addChoosableFileFilter(new QDFileFilter()); if (fc.showDialog(QDShell.this, messages.getString("OpenTranscript")) == JFileChooser.APPROVE_OPTION) { @@ -176,38 +181,24 @@ public class QDShell extends JFrame { } }); -/* - 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) { - getSave(); + qd.saveTranscript(); } }); + JMenuItem quitItem = new JMenuItem(messages.getString("Quit")); quitItem.addActionListener(new ThdlActionListener() { public void theRealActionPerformed(ActionEvent e) { - if (getWantSave()) - getSave(); + qd.saveTranscript(); System.exit(0); } }); + projectMenu.add(newItem); - projectMenu.addSeparator(); - projectMenu.add(openVideoItem); - projectMenu.add(openURLItem); + projectMenu.addSeparator(); projectMenu.add(openItem); - projectMenu.addSeparator(); -// projectMenu.add(closeItem); -// projectMenu.addSeparator(); projectMenu.add(saveItem); projectMenu.addSeparator(); projectMenu.add(quitItem); @@ -215,48 +206,8 @@ public class QDShell extends JFrame { //Tibetan-specific value: remove in non-Tibetan version //non-Tibetan specific version would have Transcription Language option here instead - -/* Note that on the Mac, you radio buttons don't appear possible as - submenus on menu bars. They just don't show up. - - 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(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")); + /* + JMenu keyboardMenu = new JMenu(messages.getString("Keyboard")); for (int i = 0; i < keybdMgr.size(); i++) { final JskadKeyboard kbd = keybdMgr.elementAt(i); @@ -269,7 +220,7 @@ public class QDShell extends JFrame { }); keyboardMenu.add(keybdItem); } - } + }*/ JMenu mediaPlayerMenu = new JMenu(messages.getString("MediaPlayer")); java.util.List moviePlayers = SmartPlayerFactory.getAllAvailableSmartPlayers(); @@ -285,7 +236,7 @@ public class QDShell extends JFrame { } JMenu preferencesMenu = new JMenu(messages.getString("Preferences")); - preferencesMenu.add(keyboardMenu); + // preferencesMenu.add(keyboardMenu); if (moviePlayers.size() > 0) { SmartMoviePanel defaultPlayer = (SmartMoviePanel)moviePlayers.get(0); qd.setMediaPlayer(defaultPlayer); //set qd media player to default @@ -293,43 +244,14 @@ public class QDShell extends JFrame { preferencesMenu.add(mediaPlayerMenu); } + JMenu[] configMenus = qd.getConfiguredMenus(); JMenuBar bar = new JMenuBar(); bar.add(projectMenu); + for (int i=0; i e.getOffset()) { + javax.swing.text.Document d = e.getDocument(); + try { + startOffsets.put(editingNode, d.createPosition(e.getOffset())); + } catch (BadLocationException ble) { + ble.printStackTrace(); + } + } + } + public void removeUpdate(DocumentEvent e) { + hasChanged = true; + } + }; + render(); + + textCursor = new Cursor(Cursor.TEXT_CURSOR); + defaultCursor = new Cursor(Cursor.DEFAULT_CURSOR); + + pane.addMouseMotionListener(new MouseMotionAdapter() { + public void mouseMoved(MouseEvent e) { + JTextPane p = (JTextPane)e.getSource(); + int offset = p.viewToModel(e.getPoint()); + if (isEditable(offset)) p.setCursor(textCursor); + else p.setCursor(defaultCursor); + } + }); + + MouseListener[] listeners = (MouseListener[])pane.getListeners(MouseListener.class); + for (int i=0; i-1); + if (dot == -1) return; //what to do? there's nothing to edit in this pane + } + if (isEditable(dot)) { + pane.getCaret().setDot(dot); + if (getNodeForOffset(dot) != null) fireStartEditEvent(getNodeForOffset(dot)); + } + } else if (editingNode == null) //need to start editing because cursor happens to be on an editable node + fireStartEditEvent(getNodeForOffset(dot)); + } + }; + + Action nextNodeAction = new AbstractAction() { + public void actionPerformed(ActionEvent e) { + JTextPane p = (JTextPane)e.getSource(); + int prePos = p.getCaretPosition(); + Object node = getNodeForOffset(prePos); + int i = prePos+1; + while (i first) p.getCaret().moveDot(offset--); + } + }; + + Action selectToNodeEndAction = new AbstractAction() { + public void actionPerformed(ActionEvent e) { + JTextPane p = (JTextPane)e.getSource(); + Object node = getNodeForOffset(p.getCaret().getMark()); + if (node != null) { + int last = (((Position)endOffsets.get(node)).getOffset()); + if (node instanceof Attribute) last--; + p.getCaret().moveDot(last); + } + } + }; + + Action selectToNodeStartAction = new AbstractAction() { + public void actionPerformed(ActionEvent e) { + JTextPane p = (JTextPane)e.getSource(); + int offset = p.getCaretPosition(); + Object node = getNodeForOffset(p.getCaret().getMark()); + if (node != null) { + int first = (((Position)startOffsets.get(node)).getOffset()); + p.getCaret().moveDot(first); + } + } + }; + + Action backwardAction = new AbstractAction() { + public void actionPerformed(ActionEvent e) { + JTextPane p = (JTextPane)e.getSource(); + int prePos = p.getCaretPosition(); + int newPos = prePos-1; + while (newPos>-1 && !isEditable(newPos)) newPos--; + if (newPos != -1) { + if (getNodeForOffset(prePos) != getNodeForOffset(newPos)) { + fireEndEditEvent(); + fireStartEditEvent(getNodeForOffset(newPos)); + } + p.setCaretPosition(newPos); + } + } + }; + + Action forwardAction = new AbstractAction() { + public void actionPerformed(ActionEvent e) { + JTextPane p = (JTextPane)e.getSource(); + int prePos = p.getCaretPosition(); + int newPos = prePos+1; + while (newPos first) { + StyledDocument d = p.getStyledDocument(); + try { + d.remove(offset-1, 1); + } catch (BadLocationException ble) { + ble.printStackTrace(); + } + } + } + }; + + Action loseFocusAction = new AbstractAction() { + public void actionPerformed(ActionEvent e) { + JTextPane p = (JTextPane)e.getSource(); + p.transferFocus(); //moves focus to next component + } + }; + + /* Action selForwardAction = new AbstractAction() { + public void actionPerformed(ActionEvent e) { + JTextPane p = (JTextPane)e.getSource(); + int offset = p.getCaretPosition(); + + } + }; + */ + createActionTable(pane); + JTextPane tp = new JTextPane(); + Keymap keymap = tp.addKeymap("KeyBindings", tp.getKeymap()); + + KeyStroke[] selectAllKeys = keymap.getKeyStrokesForAction(getActionByName(DefaultEditorKit.selectAllAction)); + if (selectAllKeys != null) + for (int i=0; i0) q=p-1; + else q=p+1; + } + pane.setCaretPosition(q); + pane.addCaretListener(editabilityTracker); //shouldn't do if already installed + pane.setCaretPosition(p); + } + else pane.removeCaretListener(editabilityTracker); + } + public void updateNode(Object node) { + System.out.println("updating: " + node.toString()); + if (node == null) + return; + try { + if (node instanceof Text) { + int p1 = ((Position)startOffsets.get(node)).getOffset(); + int p2 = ((Position)endOffsets.get(node)).getOffset(); + String val = pane.getDocument().getText(p1, p2-p1).trim(); + Text text = (Text)node; + text.setText(val); + } else if (node instanceof Attribute) { + int p1 = ((Position)startOffsets.get(node)).getOffset(); + int p2 = ((Position)endOffsets.get(node)).getOffset()-1; //remove right quote + String val = pane.getDocument().getText(p1, p2-p1).trim(); + Attribute att = (Attribute)node; + att.setValue(val); + } + System.out.println("updated: " + node.toString()); + } catch (BadLocationException ble) { + ble.printStackTrace(); + } + } + + interface NodeEditListener extends EventListener { + public void nodeEditPerformed(NodeEditEvent ned); + } + public void addNodeEditListener(NodeEditListener ned) { + listenerList.add(NodeEditListener.class, ned); + } + public void removeNodeEditListener(NodeEditListener ned) { + listenerList.remove(NodeEditListener.class, ned); + } + class NodeEditEvent extends EventObject { + Object node; + + NodeEditEvent(Object node) { + super(node); + this.node = node; + } + public Object getNode() { + return node; + } + } + class StartEditEvent extends NodeEditEvent { + StartEditEvent(Object node) { + super(node); + } + } + class EndEditEvent extends NodeEditEvent { + public EndEditEvent(Object node) { + super(node); + } + public boolean hasBeenEdited() { + return hasChanged; + } + } + class CantEditEvent extends NodeEditEvent { + public CantEditEvent(Object node) { + super(node); + } + } + public void fireStartEditEvent(Object node) { + //see javadocs on EventListenerList for how following array is structured + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==NodeEditListener.class) + ((NodeEditListener)listeners[i+1]).nodeEditPerformed(new StartEditEvent(node)); + } + isEditing = true; + editingNode = node; + hasChanged = false; + } + public void fireEndEditEvent() { + if (!isEditing) return; + //see javadocs on EventListenerList for how following array is structured + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==NodeEditListener.class) + ((NodeEditListener)listeners[i+1]).nodeEditPerformed(new EndEditEvent(editingNode)); + } + if (hasChanged) updateNode(editingNode); + isEditing = false; + editingNode = null; + hasChanged = false; + } + public void fireCantEditEvent(Object node) { + //see javadocs on EventListenerList for how following array is structured + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==NodeEditListener.class) + ((NodeEditListener)listeners[i+1]).nodeEditPerformed(new CantEditEvent(node)); + } + } + + public void setXMLDocument(Document d, String doctype_elementName, String doctype_systemID) { + xml = d; + xml.setDocType(new DocType(doctype_elementName, doctype_systemID)); + render(); + } + public void render() { + System.out.println("Rendering the document"); + doc = pane.getStyledDocument(); + int len = doc.getLength(); + try { + if (len > 0) doc.remove(0, len); + doc.insertString(0, "\n", null); + } catch (BadLocationException ble) { + ble.printStackTrace(); + } + startOffsets.clear(); + endOffsets.clear(); + Element root = xml.getRootElement(); + renderElement(root, 0.0F, doc.getLength()); + + SimpleAttributeSet eColor = new SimpleAttributeSet(); + eColor.addAttribute("xmlnode", root); + doc.setParagraphAttributes(doc.getLength(), 1, eColor, false); + + fixOffsets(); + + doc.addDocumentListener(docListen); + pane.setCaretPosition(0); + setEditabilityTracker(true); + } + + public void fixOffsets() { + //replace Integer values in startOffsets and endOffsets with Positions + Set startKeys = startOffsets.keySet(); + Iterator iter = startKeys.iterator(); + while (iter.hasNext()) { + Object key = iter.next(); + Object obj = startOffsets.get(key); + //if (obj instanceof Position) + // startOffsets.put(key, obj); //actually we don't have to do anything here, do we + //since the startoffsets are already set!! + //else + if (obj instanceof Integer) try { + Integer val = (Integer)obj; + startOffsets.put(key, doc.createPosition(val.intValue())); + } catch (BadLocationException ble) { + ble.printStackTrace(); + } + } + Set endKeys = endOffsets.keySet(); + iter = endKeys.iterator(); + while (iter.hasNext()) { + Object key = iter.next(); + Object obj = endOffsets.get(key); + //if (obj instanceof Position) + // endOffsets.put(key, obj); //actually we don't have to do anything here, do we + //since the endoffsets are already set!! + //else + if (obj instanceof Integer) try { + Integer val = (Integer)obj; + endOffsets.put(key, doc.createPosition(val.intValue())); + } catch (BadLocationException ble) { + ble.printStackTrace(); + } + } + } + public int renderElement(Element e, float indent, int insertOffset) { + try { + Position pos = doc.createPosition(insertOffset); + SimpleAttributeSet eAttributes = new SimpleAttributeSet(); + StyleConstants.setLeftIndent(eAttributes, indent); + SimpleAttributeSet eColor = new SimpleAttributeSet(); + //StyleConstants.setLeftIndent(eColor, indent); + StyleConstants.setForeground(eColor, tagColor); + eColor.addAttribute("xmlnode", e); + if (pos.getOffset()>0) { + String s = doc.getText(pos.getOffset()-1, 1); + if (s.charAt(0)!='\n') { + AttributeSet attSet = doc.getCharacterElement(pos.getOffset()-1).getAttributes(); + doc.insertString(pos.getOffset(), "\n", attSet); + } + } + int start = pos.getOffset(); + startOffsets.put(e, new Integer(start)); + String tagDisplay; + if (tagInfo == null) tagDisplay = e.getQualifiedName(); + else tagDisplay = tagInfo.getTagDisplay(e); + doc.insertString(pos.getOffset(), tagDisplay, eColor); //insert element begin tag + if (tagInfo == null || tagInfo.areTagContentsForDisplay(e.getQualifiedName())) { + List attributes = e.getAttributes(); + Iterator iter = attributes.iterator(); + while (iter.hasNext()) { + Attribute att = (Attribute)iter.next(); + if (tagInfo == null || tagInfo.isAttributeForDisplay(att.getQualifiedName(), e.getQualifiedName())) + renderAttribute(att, pos.getOffset()); + } + doc.insertString(pos.getOffset(), ":", eColor); + doc.setParagraphAttributes(start, pos.getOffset()-start, eAttributes, false); + //doc.insertString(pos.getOffset(), "\n", null); + List list = e.getContent(); + iter = list.iterator(); + while (iter.hasNext()) { + Object next = iter.next(); + if (next instanceof Element) { + Element ne = (Element)next; + if (tagInfo == null || tagInfo.isTagForDisplay(ne.getQualifiedName())) + renderElement(ne, indent + indentIncrement, pos.getOffset()); + } else if (next instanceof Text) { + Text t = (Text)next; + if (t.getParent().getContent().size() == 1 || t.getTextTrim().length() > 0) + renderText(t, indent + indentIncrement, pos.getOffset()); + } + // Also: Comment ProcessingInstruction CDATA EntityRef + } + } + //start = pos.getOffset(); + //doc.insertString(start, "}", eColor); //insert element end tag + //doc.setParagraphAttributes(start, pos.getOffset(), eAttributes, false); + + if (pos.getOffset()>0) { + //String s = doc.getText(pos.getOffset()-1, 1); + if (doc.getText(pos.getOffset()-1,1).charAt(0)=='\n') + endOffsets.put(e, new Integer(pos.getOffset()-1)); + else + endOffsets.put(e, new Integer(pos.getOffset())); + } + //endOffsets.put(e, new Integer(pos.getOffset())); + return pos.getOffset(); + //doc.insertString(pos.getOffset(), "\n", null); + } catch (BadLocationException ble) { + ble.printStackTrace(); + return -1; + } + } + + public int renderAttribute(Attribute att, int insertOffset) { + try { + Position pos = doc.createPosition(insertOffset); + SimpleAttributeSet aColor = new SimpleAttributeSet(); + StyleConstants.setForeground(aColor, attColor); + SimpleAttributeSet tColor = new SimpleAttributeSet(); + StyleConstants.setForeground(tColor, textColor); + tColor.addAttribute("xmlnode", att); + String name = att.getQualifiedName(); + String value = att.getValue(); + if (pos.getOffset()>0) { + String s = doc.getText(pos.getOffset()-1, 1); + if (s.charAt(0)!='\n') { + AttributeSet attSet = doc.getCharacterElement(pos.getOffset()-1).getAttributes(); + doc.insertString(pos.getOffset(), " ", attSet); + } + } + String displayName; + if (tagInfo == null) displayName = att.getQualifiedName(); + else displayName = tagInfo.getAttributeDisplay(att.getQualifiedName(), att.getParent().getQualifiedName()); + doc.insertString(pos.getOffset(), displayName+"=", aColor); + startOffsets.put(att, new Integer(pos.getOffset()+1)); //add one so that begin quote is not part of attribute value + doc.insertString(pos.getOffset(), "\"" + att.getValue()+"\"", tColor); + endOffsets.put(att, new Integer(pos.getOffset())); + return pos.getOffset(); + } catch (BadLocationException ble) { + ble.printStackTrace(); + return -1; + } + } + + public int renderText(Text t, float indent, int insertOffset) { + try { + Position pos = doc.createPosition(insertOffset); + SimpleAttributeSet tAttributes = new SimpleAttributeSet(); + //StyleConstants.setLeftIndent(tAttributes, indent); + StyleConstants.setForeground(tAttributes, textColor); + tAttributes.addAttribute("xmlnode", t); + doc.insertString(pos.getOffset(), " ", tAttributes); //insert space with text attributes so first character has correct color, xmlnode attribute, etc. + String s = t.getTextTrim(); + int start = pos.getOffset(); + startOffsets.put(t, new Integer(start)); + doc.insertString(pos.getOffset(), s, tAttributes); //insert text + int end = pos.getOffset(); + endOffsets.put(t, new Integer(end)); + doc.insertString(pos.getOffset(), "\n", tAttributes); + return pos.getOffset(); + } catch (BadLocationException ble) { + ble.printStackTrace(); + return -1; + } + } + + public void removeNode(Object node) { + if (startOffsets.containsKey(node)) { //note: should recursively eliminate all sub-nodes too!! + startOffsets.remove(node); + endOffsets.remove(node); + } + } + + public Object getNodeForOffset(int offset) { + AttributeSet attSet = doc.getCharacterElement(offset).getAttributes(); + return attSet.getAttribute("xmlnode"); + } + + public int getStartOffsetForNode(Object node) { + Position pos = (Position)startOffsets.get(node); + if (pos == null) return -1; + else return pos.getOffset(); + } + + public int getEndOffsetForNode(Object node) { + Position pos = (Position)endOffsets.get(node); + if (pos == null) return -1; + else return pos.getOffset(); + } + + public boolean isEditable(int offset) { + Object node = getNodeForOffset(offset); + if ((node instanceof Text) && + (offsetgetEndOffsetForNode(node))) + return false; + else if (node instanceof Attribute && + (offsetgetEndOffsetForNode(node)-1)) + return false; + else + return isEditable(node); + } + public boolean isEditable(Object node) { + if (node == null) return false; + else if (node instanceof Element) return false; + else if (node instanceof Text) return true; + else if (node instanceof Attribute) return true; + else return false; + } + public JTextPane getTextPane() { + return pane; + } + public Document getXMLDocument() { + return xml; + } +} diff --git a/source/org/thdl/quilldriver/XMLTagInfo.java b/source/org/thdl/quilldriver/XMLTagInfo.java new file mode 100644 index 0000000..0db2c2a --- /dev/null +++ b/source/org/thdl/quilldriver/XMLTagInfo.java @@ -0,0 +1,77 @@ +package org.thdl.quilldriver; + +import java.util.HashMap; +import org.jdom.Element; +import org.thdl.quilldriver.XMLUtilities; + +public class XMLTagInfo { + private HashMap displayYesNo, displayContentsYesNo, displayAs; + private HashMap attributeDisplayYesNo, attributeDisplayAs; + + public XMLTagInfo() { + displayYesNo = new HashMap(); + displayContentsYesNo = new HashMap(); + displayAs = new HashMap(); + attributeDisplayYesNo = new HashMap(); + attributeDisplayAs = new HashMap(); + } + public boolean containsTag(String tag) { + if (displayYesNo.get(tag) == null) return false; + else return true; + } + public void addTag(String tag, Boolean display, Boolean displayContents, String displayAs) { + displayYesNo.put(tag, display); + displayContentsYesNo.put(tag, displayContents); + this.displayAs.put(tag, displayAs); + } + public void removeTag(String tag) { + displayYesNo.remove(tag); + displayContentsYesNo.remove(tag); + displayAs.remove(tag); + } + public void addAttribute(String name, String parentTag, Boolean display, String displayAs) { + String s = parentTag + "/@" + name; + attributeDisplayYesNo.put(s, display); + attributeDisplayAs.put(s, displayAs); + } + public void removeAttribute(String name, String parentTag) { + String s = parentTag + "/@" + name; + attributeDisplayYesNo.remove(s); + attributeDisplayAs.remove(s); + } + public boolean isTagForDisplay(String tag) { + Object obj = displayYesNo.get(tag); + if (obj == null) return true; + else return ((Boolean)obj).booleanValue(); + } + public boolean areTagContentsForDisplay(String tag) { + Object obj = displayContentsYesNo.get(tag); + if (obj == null) return true; + else return ((Boolean)obj).booleanValue(); + } + public String getTagDisplay(Element tag) { + String name = tag.getQualifiedName(); + Object obj = displayAs.get(name); + if (obj == null) return name; + String val = (String)obj; + if (val.startsWith("XPATH:")) { + Object node = XMLUtilities.findSingleNode(tag, val.substring(val.indexOf(':')+1)); + if (node == null) return name; + String s = XMLUtilities.getTextForNode(node); + if (s == null) return name; + else return s; + } else return val; + } + public boolean isAttributeForDisplay(String name, String parentTag) { + String s = parentTag + "/@" + name; + Object obj = attributeDisplayYesNo.get(s); + if (obj == null) return true; + else return ((Boolean)obj).booleanValue(); + } + public String getAttributeDisplay(String name, String parentTag) { + String s = parentTag + "/@" + name; + Object obj = attributeDisplayAs.get(s); + if (obj == null) return name; + else return (String)obj; + } +} diff --git a/source/org/thdl/quilldriver/XMLUtilities.java b/source/org/thdl/quilldriver/XMLUtilities.java new file mode 100644 index 0000000..4822831 --- /dev/null +++ b/source/org/thdl/quilldriver/XMLUtilities.java @@ -0,0 +1,48 @@ +package org.thdl.quilldriver; + +import org.jdom.Text; +import org.jdom.Attribute; +import org.jdom.Element; +import org.jaxen.XPath; +import org.jaxen.JaxenException; +import org.jaxen.jdom.JDOMXPath; +import java.util.List; + +public class XMLUtilities { + private XMLUtilities() {} + + public static Object findSingleNode(Object jdomNode, String xpathExpression) { + if (jdomNode == null) + return null; + try { + JDOMXPath path = new JDOMXPath(xpathExpression); + return path.selectSingleNode(jdomNode); + } catch (JaxenException je) { + je.printStackTrace(); + return null; + } + } + public static List findNodeSet(Object jdomNode, String xpathExpression) { + if (jdomNode == null) + return null; + try { + JDOMXPath path = new JDOMXPath(xpathExpression); + return path.selectNodes(jdomNode); + } catch (JaxenException je) { + je.printStackTrace(); + return null; + } + } + public static String getTextForNode(Object jdomNode) { + if (jdomNode instanceof Text) { + Text t = (Text)jdomNode; + return t.getText(); + } else if (jdomNode instanceof Attribute) { + Attribute a = (Attribute)jdomNode; + return a.getValue(); + } else if (jdomNode instanceof Element) { + Element e = (Element)jdomNode; + return e.getTextTrim(); + } else return null; + } +} diff --git a/source/org/thdl/quilldriver/XMLView.java b/source/org/thdl/quilldriver/XMLView.java new file mode 100644 index 0000000..7403ded --- /dev/null +++ b/source/org/thdl/quilldriver/XMLView.java @@ -0,0 +1,126 @@ +package org.thdl.quilldriver; + +import java.util.List; +import java.util.Iterator; +import java.util.Map; +import java.util.HashMap; +import java.util.Set; +import java.util.Collection; +import javax.swing.text.JTextComponent; +import org.jdom.Document; +import org.thdl.savant.TranscriptView; +import org.thdl.quilldriver.XMLEditor; +import org.thdl.quilldriver.XMLUtilities; + + +public class XMLView implements TranscriptView { + private XMLEditor editor; + private Map startTimeMap; + private Map endTimeMap; + private Map startOffsetMap; + private Map endOffsetMap; + private Object jdomContextNode; + private String getNodesXPath; + private String getStartXPath; + private String getEndXPath; + + public XMLView(XMLEditor editor, Object jdomContextNode, String getNodesXPath, String getStartXPath, String getEndXPath) { + this.editor = editor; + this.jdomContextNode = jdomContextNode; + this.getNodesXPath = getNodesXPath; + this.getStartXPath = getStartXPath; + this.getEndXPath = getEndXPath; + startTimeMap = new HashMap(); + endTimeMap = new HashMap(); + startOffsetMap = new HashMap(); + endOffsetMap = new HashMap(); + refresh(); + } + public void refresh(Object newContextNode) { + this.jdomContextNode = newContextNode; + refresh(); + } + public void refresh() { + startTimeMap.clear(); + endTimeMap.clear(); + startOffsetMap.clear(); + endOffsetMap.clear(); + List audioNodes = XMLUtilities.findNodeSet(jdomContextNode, getNodesXPath); + Iterator iter = audioNodes.iterator(); + while (iter.hasNext()) { + Object node = iter.next(); + String id = String.valueOf(node.hashCode()); + Object start = XMLUtilities.findSingleNode(node, getStartXPath); + String startVal = XMLUtilities.getTextForNode(start); + Object end = XMLUtilities.findSingleNode(node, getEndXPath); + String endVal = XMLUtilities.getTextForNode(end); + int startOffset = editor.getStartOffsetForNode(node); + int endOffset = editor.getEndOffsetForNode(node); + if (!(startVal == null || endVal == null || startOffset == -1 || endOffset == -1)) { + startTimeMap.put(id, startVal); + endTimeMap.put(id, endVal); + startOffsetMap.put(id, String.valueOf(startOffset)); + endOffsetMap.put(id, String.valueOf(endOffset)); + } + } + } + public String getTitle() { + return "No Title"; + } + public JTextComponent getTextComponent() { + return (JTextComponent)editor.getTextPane(); + } + public String getIDs() { + Set idSet = startTimeMap.keySet(); + Iterator iter = idSet.iterator(); + StringBuffer idBuff = new StringBuffer(); + while (iter.hasNext()) { + idBuff.append((String)iter.next()); + idBuff.append(','); + } + return idBuff.toString(); + } + public String getT1s() { + Collection c = startTimeMap.values(); + Iterator iter = c.iterator(); + StringBuffer buff = new StringBuffer(); + while (iter.hasNext()) { + buff.append((String)iter.next()); + buff.append(','); + } + return buff.toString(); + } + public String getT2s() { + Collection c = endTimeMap.values(); + Iterator iter = c.iterator(); + StringBuffer buff = new StringBuffer(); + while (iter.hasNext()) { + buff.append((String)iter.next()); + buff.append(','); + } + return buff.toString(); + } + public String getStartOffsets() { + Collection c = startOffsetMap.values(); + Iterator iter = c.iterator(); + StringBuffer buff = new StringBuffer(); + while (iter.hasNext()) { + buff.append((String)iter.next()); + buff.append(','); + } + return buff.toString(); + } + public String getEndOffsets() { + Collection c = endOffsetMap.values(); + Iterator iter = c.iterator(); + StringBuffer buff = new StringBuffer(); + while (iter.hasNext()) { + buff.append((String)iter.next()); + buff.append(','); + } + return buff.toString(); + } + public Document getDocument() { + return editor.getXMLDocument(); + } +}