From 403f21c8db061e633348ec55adabddf0eeb64496 Mon Sep 17 00:00:00 2001 From: dchandler Date: Sun, 6 Oct 2002 18:23:27 +0000 Subject: [PATCH] Added Javadoc files overview.html and several package.html files. Added a "Quit" option to Savant's File menu. Factored out the Close option in doing so. Exceptions in many action listeners are now handled by org.thdl.util.ThdlActionListener or org.thdl.util.ThdlAbstractAction. Many exceptions that we used to just log now optionally cause aborts. This option is on by default for developers using 'ant savant-run'-style targets, but it is off for users. An erroneous CLASSPATH now causes a useful error message in almost all situations. Fixed some typos and bad links in Javadoc comments. Added a simple assertion facility, but the overhead is suffered even in release builds. Factored out the code that sets up log files like savant.log and jskad.log. --- source/org/thdl/quilldriver/QD.java | 107 +- source/org/thdl/quilldriver/QDPlayer.java | 4 + source/org/thdl/quilldriver/QDShell.java | 76 +- source/org/thdl/quilldriver/package.html | 27 + source/org/thdl/savant/JdkVersionHacks.java | 39 +- source/org/thdl/savant/Savant.java | 10 +- source/org/thdl/savant/SavantFileView.java | 61 +- source/org/thdl/savant/SavantShell.java | 220 +- source/org/thdl/savant/SoundPanel.java | 4 + .../org/thdl/savant/TextHighlightPlayer.java | 3 + source/org/thdl/savant/TextPlayer.java | 3 + source/org/thdl/savant/TwoWayTextPlayer.java | 3 + source/org/thdl/savant/package.html | 24 + source/org/thdl/savant/tib/English.java | 4 + source/org/thdl/savant/tib/Tibetan.java | 5 + .../org/thdl/savant/tib/TibetanEnglish.java | 5 + source/org/thdl/savant/tib/TibetanWylie.java | 5 + .../thdl/savant/tib/TibetanWylieEnglish.java | 5 + source/org/thdl/savant/tib/Wylie.java | 4 + source/org/thdl/savant/tib/WylieEnglish.java | 4 + source/org/thdl/savant/tib/package.html | 23 + source/org/thdl/savant/ucuchi/All.java | 4 + source/org/thdl/savant/ucuchi/English.java | 4 + source/org/thdl/savant/ucuchi/Quechua.java | 4 + .../thdl/savant/ucuchi/QuechuaEnglish.java | 4 + .../thdl/savant/ucuchi/SegmentedQuechua.java | 4 + source/org/thdl/savant/ucuchi/package.html | 23 + source/org/thdl/tib/input/DuffPane.java | 15 + source/org/thdl/tib/input/Jskad.java | 94 +- .../org/thdl/tib/input/Jskad2JavaScript.java | 8 +- .../thdl/tib/input/JskadConversionTool.java | 8 +- source/org/thdl/tib/input/package.html | 70 +- source/org/thdl/tib/scanner/AboutDialog.java | 5 +- .../thdl/tib/scanner/AppletScannerFilter.java | 5 +- .../thdl/tib/scanner/DictionarySource.java | 4 +- .../tib/scanner/FileSyllableListTree.java | 8 +- source/org/thdl/tib/scanner/ScannerPanel.java | 5 +- .../thdl/tib/scanner/WindowScannerFilter.java | 5 +- source/org/thdl/tib/scanner/package.html | 21 + source/org/thdl/tib/text/TibetanDocument.java | 2559 +++++++++-------- source/org/thdl/tib/text/package.html | 4 +- source/org/thdl/util/TeeStream.java | 4 +- source/org/thdl/util/ThdlAbstractAction.java | 85 + source/org/thdl/util/ThdlActionListener.java | 157 + source/org/thdl/util/ThdlDebug.java | 136 + source/org/thdl/util/package.html | 22 + source/overview.html | 112 + 47 files changed, 2439 insertions(+), 1567 deletions(-) create mode 100644 source/org/thdl/quilldriver/package.html create mode 100644 source/org/thdl/savant/package.html create mode 100644 source/org/thdl/savant/tib/package.html create mode 100644 source/org/thdl/savant/ucuchi/package.html create mode 100644 source/org/thdl/tib/scanner/package.html create mode 100644 source/org/thdl/util/ThdlAbstractAction.java create mode 100644 source/org/thdl/util/ThdlActionListener.java create mode 100644 source/org/thdl/util/ThdlDebug.java create mode 100644 source/org/thdl/util/package.html create mode 100644 source/overview.html diff --git a/source/org/thdl/quilldriver/QD.java b/source/org/thdl/quilldriver/QD.java index e217b51..4e05558 100644 --- a/source/org/thdl/quilldriver/QD.java +++ b/source/org/thdl/quilldriver/QD.java @@ -41,8 +41,16 @@ import org.jdom.transform.JDOMSource; import javax.xml.transform.stream.*; import javax.xml.transform.*; +import org.thdl.util.ThdlDebug; +import org.thdl.util.ThdlActionListener; +import org.thdl.util.ThdlAbstractAction; + 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; @@ -97,16 +105,16 @@ public QD(ResourceBundle messages) { fileChooser = new JFileChooser(); fileChooser.addChoosableFileFilter(new XMLFilter()); - Action insert1TimeAction = new AbstractAction("insert1Time", null) { - public void actionPerformed(ActionEvent e) { + Action insert1TimeAction = new ThdlAbstractAction("insert1Time", null) { + public void theRealActionPerformed(ActionEvent e) { new TimePoint(pane, clockIcon, tcp.getOutTime()); tcp.setInTime(tcp.getOutTime().intValue()); tcp.setOutTime(player.getLastTime()); } }; - Action insert2TimesAction = new AbstractAction("insert2Times", null) { - public void actionPerformed(ActionEvent e) { + Action insert2TimesAction = new ThdlAbstractAction("insert2Times", null) { + public void theRealActionPerformed(ActionEvent e) { int p1 = pane.getSelectionStart(); int p2 = pane.getSelectionEnd(); pane.setCaretPosition(p1); @@ -120,8 +128,8 @@ public QD(ResourceBundle messages) { } }; - Action saveAction = new AbstractAction("saveTranscript", null) { - public void actionPerformed(ActionEvent e) { + Action saveAction = new ThdlAbstractAction("saveTranscript", null) { + public void theRealActionPerformed(ActionEvent e) { getSave(); } }; @@ -282,6 +290,7 @@ class TimePoint extends JLabel implements DragGestureListener, DragSourceListene pos = doc.createPosition(tp.getCaretPosition()-1); } catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } } public void setTime(Integer t) { @@ -315,6 +324,7 @@ class TimePoint extends JLabel implements DragGestureListener, DragSourceListene dge.startDrag(null, transferable, this); } catch (InvalidDnDOperationException idoe) { idoe.printStackTrace(); + ThdlDebug.noteIffyCode(); } } public void dragEnter(DragSourceDragEvent dsde) { @@ -332,6 +342,7 @@ class TimePoint extends JLabel implements DragGestureListener, DragSourceListene doc.remove(pos.getOffset(), 1); } catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } } } @@ -400,9 +411,11 @@ public class TimePointDropTarget implements DropTargetListener { new TimePoint(pane, clockIcon, time); } catch (UnsupportedFlavorException ufe) { ufe.printStackTrace(); + ThdlDebug.noteIffyCode(); return false; } catch (IOException ioe) { ioe.printStackTrace(); + ThdlDebug.noteIffyCode(); return false; } return true; @@ -423,8 +436,8 @@ class TimeCodeManager extends JPanel { inPanel.add(inButton); inPanel.add(inSpinner); - inButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + inButton.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { int k = player.when(); if (k != -1) setInTime(k); @@ -441,8 +454,8 @@ class TimeCodeManager extends JPanel { outPanel.add(outButton); outPanel.add(outSpinner); - outButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + outButton.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { int k = player.when(); if (k != -1) { setOutTime(k); @@ -453,8 +466,8 @@ class TimeCodeManager extends JPanel { JButton playSegButton = new JButton(messages.getString("PlaySegment")); - playSegButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + playSegButton.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { Integer in = getInTime(); Integer out = getOutTime(); if (out.intValue() > in.intValue()) @@ -468,14 +481,14 @@ class TimeCodeManager extends JPanel { JButton playButton = new JButton(messages.getString("Play")); JButton pauseButton = new JButton(messages.getString("Pause")); - playButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + playButton.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { if (player != null) player.cmd_play(); } }); - pauseButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + pauseButton.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { if (player != null) player.cmd_stop(); } @@ -593,6 +606,7 @@ public void setMedia(File f) { startTimer(); } catch (MalformedURLException murle) { murle.printStackTrace(); + ThdlDebug.noteIffyCode(); } } } @@ -696,7 +710,7 @@ public boolean getSave() { fileChooser.setSelectedFile(null); else { String path = project.getMedia().getPath(); - path = path.substring(0, path.lastIndexOf('.')) + ".xml"; + path = path.substring(0, path.lastIndexOf('.')) + QD.dotQuillDriver; fileChooser.setSelectedFile(new File(path)); } if (fileChooser.showSaveDialog(QD.this) == JFileChooser.APPROVE_OPTION) { @@ -838,12 +852,15 @@ if (keyboard_url != null) { return true; } catch (FileNotFoundException fnfe) { fnfe.printStackTrace(); + ThdlDebug.noteIffyCode(); return false; } catch (IOException ioe) { ioe.printStackTrace(); + ThdlDebug.noteIffyCode(); return false; } catch (TransformerException e) { e.printStackTrace(); + ThdlDebug.noteIffyCode(); return false; } } @@ -950,6 +967,7 @@ if (keyboard_url != null) { tDoc.insertString(tDoc.getLength(), "\n", null); } catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } */ new TimePoint(dp, clockIcon, Integer.valueOf(e.getAttributeValue("t"))); @@ -961,6 +979,7 @@ if (keyboard_url != null) { tDoc.insertString(tDoc.getLength(), "\n", null); } catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } */ dp.insertComponent(new JLabel(" ", icons[Integer.parseInt(e.getAttributeValue("id"))], SwingConstants.LEFT)); @@ -984,6 +1003,7 @@ project.setTranscript(t); return true; } catch (JDOMException jdome) { jdome.printStackTrace(); + ThdlDebug.noteIffyCode(); return false; } } @@ -999,8 +1019,8 @@ SpeakerManager(final SpeakerTable sTable) { setLayout(new BorderLayout()); JButton addButton = new JButton(messages.getString("Add")); - addButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + addButton.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { SpeakerData sd = (SpeakerData)sTable.getModel(); JPanel p0 = new JPanel(new GridLayout(0,1)); JPanel p1 = new JPanel(); @@ -1021,8 +1041,8 @@ SpeakerManager(final SpeakerTable sTable) { } }); JButton editButton = new JButton(messages.getString("Edit")); - editButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + editButton.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { int row = sTable.getSelectedRow(); if (row == -1) return; @@ -1062,6 +1082,7 @@ SpeakerManager(final SpeakerTable sTable) { doc2.remove(i,1); } catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } pane.setCaretPosition(i); pane.insertComponent(new JLabel(" ", icons[combo.getSelectedIndex()], SwingConstants.LEFT)); @@ -1075,8 +1096,8 @@ SpeakerManager(final SpeakerTable sTable) { } }); JButton deleteButton = new JButton(messages.getString("Remove")); - deleteButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + deleteButton.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { int row = sTable.getSelectedRow(); if (row > -1 && JOptionPane.showConfirmDialog(SpeakerManager.this, messages.getString("SureDeleteSpeaker"), messages.getString("Warning"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION) { SpeakerData sd = (SpeakerData)sTable.getModel(); @@ -1096,6 +1117,7 @@ SpeakerManager(final SpeakerTable sTable) { k2--; } catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } } } @@ -1187,8 +1209,8 @@ class SpeakerData extends AbstractTableModel for (int k=0; k + + + + + +Provides facilitates for authoring content to be used by Savant. +

+In the future, this may be combined with Savant, in which case this +will be the edit mode. For now, QuillDriver is a stand-alone +application. +

+

Related Documentation

+@see org.thdl.savant + + diff --git a/source/org/thdl/savant/JdkVersionHacks.java b/source/org/thdl/savant/JdkVersionHacks.java index 5ae18f1..f599f8f 100644 --- a/source/org/thdl/savant/JdkVersionHacks.java +++ b/source/org/thdl/savant/JdkVersionHacks.java @@ -22,6 +22,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Field; +import java.awt.Dimension; import java.awt.Frame; import java.awt.Toolkit; @@ -49,7 +50,7 @@ import org.thdl.util.ThdlLazyException; * deconstruct the mechanism. * * This class is not instantiable. */ -final class JdkVersionHacks { +public final class JdkVersionHacks { /** Don't instantiate this class. */ private JdkVersionHacks() { } @@ -182,14 +183,33 @@ final class JdkVersionHacks { } } + public static Object getJSpinner(Dimension dim, + Object spinnerValue, + int value, int minimum, + int maximum, int stepSize) { + /* In Java 1.4, do the following: + + SpinnerNumberModel snm1 + = new SpinnerNumberModel(value, minimum, maximum, stepSize); + JSpinner spinner = new JSpinner(snm1); + spinner.setPreferredSize(dim); + spinner.setValue(spinnerValue); + return spinner; + */ + /* FIXME; */ + return null; + } + + + /** Coming soon: Does what the user desires (via the options he or - she has set) with this SecurityException, one encountered - during the process of reflection. + she has set) with this SecurityException, one encountered + during the process of reflection. - Currently: does nothing. + Currently: does nothing. - FIXME */ + FIXME */ private static void handleSecurityException(SecurityException ex) throws SecurityException { @@ -198,10 +218,10 @@ final class JdkVersionHacks { } /** Returns the value of Frame.MAXIMIZED_BOTH, wrapped in an - Integer. + Integer. - @throws NoSuchFieldException the field does not exist or - cannot be accessed because security settings are too limiting */ + @throws NoSuchFieldException the field does not exist or + cannot be accessed because security settings are too limiting */ private static Object maximizedBothOption() throws NoSuchFieldException { @@ -209,7 +229,7 @@ final class JdkVersionHacks { try { maxBothOptionField = Frame.class.getField("MAXIMIZED_BOTH"); - /* Don't catch NoSuchFieldException */ + /* Don't catch NoSuchFieldException */ } catch (SecurityException ex) { /* We'll never know if we're using JDK 1.4 or later. */ handleSecurityException(ex); @@ -229,3 +249,4 @@ final class JdkVersionHacks { } } }; + diff --git a/source/org/thdl/savant/Savant.java b/source/org/thdl/savant/Savant.java index 1f9094b..d821b96 100644 --- a/source/org/thdl/savant/Savant.java +++ b/source/org/thdl/savant/Savant.java @@ -59,6 +59,9 @@ import javax.swing.*; import javax.swing.text.*; import javax.swing.text.rtf.RTFEditorKit; +import org.thdl.util.ThdlDebug; +import org.thdl.util.ThdlActionListener; + public class Savant extends JDesktopPane { protected SoundPanel sp = null; @@ -142,6 +145,7 @@ public class Savant extends JDesktopPane open(views, new URL(video), new URL(vocabulary)); } catch (MalformedURLException murle) { murle.printStackTrace(); + ThdlDebug.noteIffyCode(); } } @@ -159,8 +163,8 @@ public class Savant extends JDesktopPane viewNames[i] = new String(views[i].getTitle()); JComboBox viewOptions = new JComboBox(viewNames); - viewOptions.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) + viewOptions.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { JComboBox jcb = (JComboBox)e.getSource(); setTranscriptView(views[jcb.getSelectedIndex()]); @@ -201,8 +205,10 @@ public class Savant extends JDesktopPane vocabPane.getCaret().setDot(0); } catch (IOException ioe) { ioe.printStackTrace(); + ThdlDebug.noteIffyCode(); } catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } vocabFrame = new JInternalFrame("About the Video", false, false, false, true); vocabFrame.setContentPane(vocabPanel); diff --git a/source/org/thdl/savant/SavantFileView.java b/source/org/thdl/savant/SavantFileView.java index 9d8d0da..3e41a9c 100644 --- a/source/org/thdl/savant/SavantFileView.java +++ b/source/org/thdl/savant/SavantFileView.java @@ -23,30 +23,85 @@ import java.util.*; import javax.swing.*; import javax.swing.filechooser.*; +import org.thdl.util.ThdlDebug; + +/** + * The SavantFileView "sees through" a *.savant file and + * returns the title associated with it. A *.savant file + * is a properties file like the following: + *
+ * #Tue May 14 16:07:15 EDT 2002
+ * TRANSCRIPT=MST4.xml
+ * PROJECT=THDL
+ * MEDIA=MST4.mpg
+ * TITLE=MST Chapter 4 (video)
+ * 
+ * @author Edward Garrett */ public class SavantFileView extends FileView { + /** When opening a file, this is the only extension Savant cares + about. This is case-insensitive. */ + public final static String dotSavant = ".savant"; + + /** This loads *.savant files as properties files and + returns an associated TITLE attribute. For any other type of + file, or for a properties file that does not specify a + non-empty-string TITLE attribute, this returns null. */ public String getName(File f) { if (f.isDirectory()) return null; + + /* Unless a developer has chosen to do otherwise, examine only + *.savant files. If you don't do this, you waste a lot of + time, making any file chooser that uses this class + unresponsive. In addition, you'll cause the floppy drive + to spin up every time you refresh or cd in the file + chooser. */ + if (!Boolean.getBoolean("THDL_TREAT_ALL_FILES_AS_DOT_SAVANT_FILES_REGARDLESS_OF_EXTENSION")) { /* FIXME */ + if (!f.getName().toLowerCase().endsWith(dotSavant)) + return null; + } Properties p = new Properties(); try { p.load(new FileInputStream(f)); - return p.getProperty("TITLE"); + String ttl = p.getProperty("TITLE"); + if (ttl.equals("")) + return null; /* We never want the user to see nothing + at all, so let the L&F file view + handle this. */ + else + return ttl; } catch (FileNotFoundException fnfe) { fnfe.printStackTrace(); + ThdlDebug.noteIffyCode(); } catch (IOException ioe) { ioe.printStackTrace(); + ThdlDebug.noteIffyCode(); } return null; } + + /** Returns null always, which lets the default look-and-feel's + * FileView take over. + * @return null */ public String getDescription(File f) { - return null; + return null; // let the L&F FileView figure this out } + + /** Returns null always, which lets the default look-and-feel's + * FileView take over. + * @return null */ public Boolean isTraversable(File f) { return null; // let the L&F FileView figure this out } + + /** Returns null always, which lets the default look-and-feel's + * FileView take over. + * @return null */ public String getTypeDescription(File f) { - return null; + return null; // let the L&F FileView figure this out } + + /* FIXME: why not same as above? */ /* public Icon getIcon(File f) { } diff --git a/source/org/thdl/savant/SavantShell.java b/source/org/thdl/savant/SavantShell.java index b6aa1ae..228b7b0 100644 --- a/source/org/thdl/savant/SavantShell.java +++ b/source/org/thdl/savant/SavantShell.java @@ -30,6 +30,8 @@ import javax.swing.text.rtf.*; import org.thdl.savant.ucuchi.*; import org.thdl.savant.tib.*; import org.thdl.util.TeeStream; +import org.thdl.util.ThdlDebug; +import org.thdl.util.ThdlActionListener; public class SavantShell extends JFrame { @@ -44,56 +46,49 @@ public class SavantShell extends JFrame public static void main(String[] args) { try { - PrintStream psOut - = new TeeStream(System.out, - new PrintStream(new FileOutputStream("savant.log"))); - PrintStream psErr - = new TeeStream(System.err, - new PrintStream(new FileOutputStream("savant.log"))); - System.setErr(psErr); - System.setOut(psOut); - } - catch (Exception e) { - } + ThdlDebug.attemptToSetUpLogFile("savant.log"); - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (Exception e) {} + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) {} - RTFEditorKit rtf = new RTFEditorKit(); - InputStream in1 = SavantShell.class.getResourceAsStream("savanthelp.rtf"); - InputStream in2 = SavantShell.class.getResourceAsStream("aboutsavant.rtf"); - if (in1 == null) { - System.out.println("Can't find savanthelp.rtf."); - System.exit(1); + RTFEditorKit rtf = new RTFEditorKit(); + InputStream in1 = SavantShell.class.getResourceAsStream("savanthelp.rtf"); + InputStream in2 = SavantShell.class.getResourceAsStream("aboutsavant.rtf"); + if (in1 == null) { + System.out.println("Can't find savanthelp.rtf."); + System.exit(1); + } + if (in2 == null) { + System.out.println("Can't find aboutsavant.rtf."); + System.exit(1); + } + DefaultStyledDocument doc1 = new DefaultStyledDocument(); + DefaultStyledDocument doc2 = new DefaultStyledDocument(); + try { + rtf.read(in1, doc1, 0); + rtf.read(in2, doc2, 0); + } catch (BadLocationException ioe) { + return; + } catch (IOException ioe) { + System.out.println("Can't find one of savanthelp.rtf or aboutsavant.rtf."); + System.exit(1); + } + + JTextPane pane1 = new JTextPane(doc1); + JTextPane pane2 = new JTextPane(doc2); + pane1.setEditable(false); + pane2.setEditable(false); + + helpPane = new JScrollPane(pane1); + aboutPane = new JScrollPane(pane2); + + SavantShell ssh = new SavantShell(); + ssh.setVisible(true); + ssh.initFileChooser(); + } catch (NoClassDefFoundError err) { + ThdlDebug.handleClasspathError("Savant's CLASSPATH", err); } - if (in2 == null) { - System.out.println("Can't find aboutsavant.rtf."); - System.exit(1); - } - DefaultStyledDocument doc1 = new DefaultStyledDocument(); - DefaultStyledDocument doc2 = new DefaultStyledDocument(); - try { - rtf.read(in1, doc1, 0); - rtf.read(in2, doc2, 0); - } catch (BadLocationException ioe) { - return; - } catch (IOException ioe) { - System.out.println("Can't find one of savanthelp.rtf or aboutsavant.rtf."); - System.exit(1); - } - - JTextPane pane1 = new JTextPane(doc1); - JTextPane pane2 = new JTextPane(doc2); - pane1.setEditable(false); - pane2.setEditable(false); - - helpPane = new JScrollPane(pane1); - aboutPane = new JScrollPane(pane2); - - SavantShell ssh = new SavantShell(); - ssh.setVisible(true); - ssh.initFileChooser(); } public void initFileChooser() { @@ -114,21 +109,7 @@ public class SavantShell extends JFrame if (f.isDirectory()) { return true; } - - String fName = f.getName(); - int i = fName.lastIndexOf('.'); - - if (i < 0) - return false; - - else { - String ext = fName.substring(i+1).toLowerCase(); - - if (ext.equals("savant")) - return true; - else - return false; - } + return f.getName().toLowerCase().endsWith(SavantFileView.dotSavant); } //the description of this filter @@ -137,6 +118,33 @@ public class SavantShell extends JFrame } } + /** Closes one open title, if there is one open. Returns true if + one was closed, or false if no titles are open. */ + private boolean closeOneSavantTitle() { + if (savant != null) { + ThdlDebug.verify("There should be one or more Savant titles open: ", + numberOfSavantsOpen >= 1); + if (numberOfSavantsOpen < 2) { + savant.close(); + savant = null; + getContentPane().removeAll(); + setTitle("Savant"); + invalidate(); + validate(); + repaint(); + } else { + savant.close(); + dispose(); + } + numberOfSavantsOpen--; + return true; + } else { + ThdlDebug.verify("There should be zero Savant titles open: ", + numberOfSavantsOpen == 0); + return false; + } + } + public SavantShell() { setTitle("Savant"); @@ -146,8 +154,8 @@ public class SavantShell extends JFrame JMenuItem openItem = new JMenuItem("Open"); openItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,2)); - openItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + openItem.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { if (fileChooser.showOpenDialog(SavantShell.this) != JFileChooser.APPROVE_OPTION) return; Properties p = new Properties(); @@ -158,48 +166,66 @@ public class SavantShell extends JFrame newSavantWindow(p.getProperty("PROJECT"), p.getProperty("TITLE"), new URL(path + p.getProperty("TRANSCRIPT")), new URL(path + p.getProperty("MEDIA")), null); } catch (MalformedURLException murle) { murle.printStackTrace(); + ThdlDebug.noteIffyCode(); } catch (FileNotFoundException fnfe) { fnfe.printStackTrace(); + ThdlDebug.noteIffyCode(); } catch (IOException ioe) { ioe.printStackTrace(); + ThdlDebug.noteIffyCode(); } } }); JMenuItem closeItem = new JMenuItem("Close"); closeItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C,2)); - closeItem.addActionListener(new ActionListener() + closeItem.addActionListener(new ThdlActionListener() { - public void actionPerformed(ActionEvent e) + public void theRealActionPerformed(ActionEvent e) { - if (savant != null) - { - if (numberOfSavantsOpen < 2) - { - savant.close(); - savant = null; - getContentPane().removeAll(); - setTitle("Savant"); - invalidate(); - validate(); - repaint(); - } else { - savant.close(); - dispose(); - } - numberOfSavantsOpen--; - } + // Return value doesn't matter: + closeOneSavantTitle(); + } + }); + + JMenuItem quitItem = new JMenuItem("Quit"); + quitItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q,2)); + quitItem.addActionListener(new ThdlActionListener() + { + public void theRealActionPerformed(ActionEvent e) + { + // Close all Savant titles: + while (closeOneSavantTitle()) + ; + + // Exit normally: + System.exit(0); } }); + /* Hey developers: To test ThdlActionListener, use this: + + JMenuItem errorThrowerItem = new JMenuItem("ErrorThrower"); + errorThrowerItem.addActionListener(new ThdlActionListener() + { + public void theRealActionPerformed(ActionEvent e) + { + throw new Error("Testing ThdlActionListener"); + } + }); + fileMenu.add(errorThrowerItem); + */ + fileMenu.add(openItem); fileMenu.add(closeItem); + fileMenu.addSeparator(); + fileMenu.add(quitItem); JMenu infoMenu = new JMenu("Information"); JMenuItem helpItem = new JMenuItem("Help"); - helpItem.addActionListener(new ActionListener() + helpItem.addActionListener(new ThdlActionListener() { - public void actionPerformed(ActionEvent e) + public void theRealActionPerformed(ActionEvent e) { JFrame h = new JFrame("Help"); h.setSize(500,400); @@ -218,9 +244,9 @@ public class SavantShell extends JFrame } }); JMenuItem aboutItem = new JMenuItem("About"); - aboutItem.addActionListener(new ActionListener() + aboutItem.addActionListener(new ThdlActionListener() { - public void actionPerformed(ActionEvent e) + public void theRealActionPerformed(ActionEvent e) { JFrame h = new JFrame("About"); h.setSize(500,400); @@ -286,15 +312,19 @@ public class SavantShell extends JFrame public void newSavantWindow(String project, String titleName, URL trn, URL vid, URL abt) { - if (numberOfSavantsOpen == 0) - openSavant(project, titleName, trn, vid, abt); - else { - SavantShell scp = new SavantShell(); - scp.setVisible(true); - scp.openSavant(project, titleName, trn, vid, abt); - scp.setFileChooser(fileChooser); + try { + if (numberOfSavantsOpen == 0) + openSavant(project, titleName, trn, vid, abt); + else { + SavantShell scp = new SavantShell(); + scp.setVisible(true); + scp.openSavant(project, titleName, trn, vid, abt); + scp.setFileChooser(fileChooser); + } + numberOfSavantsOpen++; + } catch (NoClassDefFoundError err) { + ThdlDebug.handleClasspathError("Savant's CLASSPATH", err); } - numberOfSavantsOpen++; } public void openSavant(String project, String titleName, URL trn, URL vid, URL abt) diff --git a/source/org/thdl/savant/SoundPanel.java b/source/org/thdl/savant/SoundPanel.java index 76634d0..fb8bfc6 100644 --- a/source/org/thdl/savant/SoundPanel.java +++ b/source/org/thdl/savant/SoundPanel.java @@ -26,6 +26,8 @@ import java.awt.*; import javax.swing.*; import javax.swing.event.*; +import org.thdl.util.ThdlDebug; + /*-----------------------------------------------------------------------*/ public class SoundPanel extends Panel implements ControllerListener { @@ -346,10 +348,12 @@ System.out.println("soundpanel - "+sound.toString()); } catch (javax.media.NoPlayerException e) { System.err.println("noplayer exception"); e.printStackTrace(); + ThdlDebug.noteIffyCode(); return; } catch (java.io.IOException ex) { System.err.println("IO exception"); ex.printStackTrace(); + ThdlDebug.noteIffyCode(); return; } if (player != null) diff --git a/source/org/thdl/savant/TextHighlightPlayer.java b/source/org/thdl/savant/TextHighlightPlayer.java index 54a4053..87c0452 100644 --- a/source/org/thdl/savant/TextHighlightPlayer.java +++ b/source/org/thdl/savant/TextHighlightPlayer.java @@ -24,6 +24,8 @@ import javax.swing.*; import javax.swing.text.*; import java.awt.event.MouseListener; +import org.thdl.util.ThdlDebug; + public class TextHighlightPlayer extends JPanel implements AnnotationPlayer { protected JTextComponent text; @@ -108,6 +110,7 @@ public class TextHighlightPlayer extends JPanel implements AnnotationPlayer catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } } diff --git a/source/org/thdl/savant/TextPlayer.java b/source/org/thdl/savant/TextPlayer.java index 849122b..dbac382 100644 --- a/source/org/thdl/savant/TextPlayer.java +++ b/source/org/thdl/savant/TextPlayer.java @@ -23,6 +23,8 @@ import java.util.*; import javax.swing.*; import javax.swing.text.*; +import org.thdl.util.ThdlDebug; + public class TextPlayer extends JPanel implements AnnotationPlayer { private JTextComponent text; @@ -97,6 +99,7 @@ public class TextPlayer extends JPanel implements AnnotationPlayer catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } } diff --git a/source/org/thdl/savant/TwoWayTextPlayer.java b/source/org/thdl/savant/TwoWayTextPlayer.java index 014c294..f73cc18 100644 --- a/source/org/thdl/savant/TwoWayTextPlayer.java +++ b/source/org/thdl/savant/TwoWayTextPlayer.java @@ -30,6 +30,8 @@ import java.util.Enumeration; import java.util.NoSuchElementException; import javax.swing.text.JTextComponent; +import org.thdl.util.ThdlDebug; + public class TwoWayTextPlayer extends TextHighlightPlayer { protected TreeMap orderedOffsets = null; @@ -106,6 +108,7 @@ public class TwoWayTextPlayer extends TextHighlightPlayer catch (NoSuchElementException nsee) { nsee.printStackTrace(); + ThdlDebug.noteIffyCode(); } } }); diff --git a/source/org/thdl/savant/package.html b/source/org/thdl/savant/package.html new file mode 100644 index 0000000..02f8920 --- /dev/null +++ b/source/org/thdl/savant/package.html @@ -0,0 +1,24 @@ + + + + + + +Provides classes and methods for displaying foreign-language text, +audio, and video side-by-side. +

+Savant works for only a couple of languages right now, but can +be extended to work with many more. + + diff --git a/source/org/thdl/savant/tib/English.java b/source/org/thdl/savant/tib/English.java index 0dda0ed..71c0a44 100644 --- a/source/org/thdl/savant/tib/English.java +++ b/source/org/thdl/savant/tib/English.java @@ -32,6 +32,8 @@ import org.thdl.tib.text.*; import org.thdl.tib.input.*; import org.thdl.savant.*; +import org.thdl.util.ThdlDebug; + public class English implements TranscriptView { private JTextPane text = null; @@ -60,6 +62,7 @@ public class English implements TranscriptView catch (JDOMException jdome) { jdome.printStackTrace(); + ThdlDebug.noteIffyCode(); } } @@ -174,6 +177,7 @@ public class English implements TranscriptView catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } } diff --git a/source/org/thdl/savant/tib/Tibetan.java b/source/org/thdl/savant/tib/Tibetan.java index 74cd0b7..0929a15 100644 --- a/source/org/thdl/savant/tib/Tibetan.java +++ b/source/org/thdl/savant/tib/Tibetan.java @@ -32,6 +32,8 @@ import org.thdl.tib.text.*; import org.thdl.tib.input.*; import org.thdl.savant.*; +import org.thdl.util.ThdlDebug; + public class Tibetan implements TranscriptView { private JTextPane text = null; @@ -60,6 +62,7 @@ public class Tibetan implements TranscriptView catch (JDOMException jdome) { jdome.printStackTrace(); + ThdlDebug.noteIffyCode(); } } @@ -179,10 +182,12 @@ public class Tibetan implements TranscriptView catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } catch (InvalidWylieException iwe) { iwe.printStackTrace(); + ThdlDebug.noteIffyCode(); } } diff --git a/source/org/thdl/savant/tib/TibetanEnglish.java b/source/org/thdl/savant/tib/TibetanEnglish.java index 4e4414b..0579f63 100644 --- a/source/org/thdl/savant/tib/TibetanEnglish.java +++ b/source/org/thdl/savant/tib/TibetanEnglish.java @@ -32,6 +32,8 @@ import org.thdl.tib.text.*; import org.thdl.tib.input.*; import org.thdl.savant.*; +import org.thdl.util.ThdlDebug; + public class TibetanEnglish implements TranscriptView { private JTextPane text = null; @@ -60,6 +62,7 @@ public class TibetanEnglish implements TranscriptView catch (JDOMException jdome) { jdome.printStackTrace(); + ThdlDebug.noteIffyCode(); } } @@ -187,10 +190,12 @@ public class TibetanEnglish implements TranscriptView catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } catch (InvalidWylieException iwe) { iwe.printStackTrace(); + ThdlDebug.noteIffyCode(); } } diff --git a/source/org/thdl/savant/tib/TibetanWylie.java b/source/org/thdl/savant/tib/TibetanWylie.java index fd6f1b9..c3dfa45 100644 --- a/source/org/thdl/savant/tib/TibetanWylie.java +++ b/source/org/thdl/savant/tib/TibetanWylie.java @@ -32,6 +32,8 @@ import org.thdl.tib.text.*; import org.thdl.tib.input.*; import org.thdl.savant.*; +import org.thdl.util.ThdlDebug; + public class TibetanWylie implements TranscriptView { private JTextPane text = null; @@ -60,6 +62,7 @@ public class TibetanWylie implements TranscriptView catch (JDOMException jdome) { jdome.printStackTrace(); + ThdlDebug.noteIffyCode(); } } @@ -190,10 +193,12 @@ public class TibetanWylie implements TranscriptView catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } catch (InvalidWylieException iwe) { iwe.printStackTrace(); + ThdlDebug.noteIffyCode(); } } diff --git a/source/org/thdl/savant/tib/TibetanWylieEnglish.java b/source/org/thdl/savant/tib/TibetanWylieEnglish.java index 4caa621..4a73a6b 100644 --- a/source/org/thdl/savant/tib/TibetanWylieEnglish.java +++ b/source/org/thdl/savant/tib/TibetanWylieEnglish.java @@ -32,6 +32,8 @@ import org.thdl.tib.text.*; import org.thdl.tib.input.*; import org.thdl.savant.*; +import org.thdl.util.ThdlDebug; + public class TibetanWylieEnglish implements TranscriptView { private JTextPane text = null; @@ -60,6 +62,7 @@ public class TibetanWylieEnglish implements TranscriptView catch (JDOMException jdome) { jdome.printStackTrace(); + ThdlDebug.noteIffyCode(); } } @@ -192,10 +195,12 @@ public class TibetanWylieEnglish implements TranscriptView catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } catch (InvalidWylieException iwe) { iwe.printStackTrace(); + ThdlDebug.noteIffyCode(); } } diff --git a/source/org/thdl/savant/tib/Wylie.java b/source/org/thdl/savant/tib/Wylie.java index 5b9a189..58e9e39 100644 --- a/source/org/thdl/savant/tib/Wylie.java +++ b/source/org/thdl/savant/tib/Wylie.java @@ -32,6 +32,8 @@ import org.thdl.tib.text.*; import org.thdl.tib.input.*; import org.thdl.savant.*; +import org.thdl.util.ThdlDebug; + public class Wylie implements TranscriptView { private JTextPane text = null; @@ -60,6 +62,7 @@ public class Wylie implements TranscriptView catch (JDOMException jdome) { jdome.printStackTrace(); + ThdlDebug.noteIffyCode(); } } @@ -174,6 +177,7 @@ public class Wylie implements TranscriptView catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } } diff --git a/source/org/thdl/savant/tib/WylieEnglish.java b/source/org/thdl/savant/tib/WylieEnglish.java index 36c2229..4568fce 100644 --- a/source/org/thdl/savant/tib/WylieEnglish.java +++ b/source/org/thdl/savant/tib/WylieEnglish.java @@ -32,6 +32,8 @@ import org.thdl.tib.text.*; import org.thdl.tib.input.*; import org.thdl.savant.*; +import org.thdl.util.ThdlDebug; + public class WylieEnglish implements TranscriptView { private JTextPane text = null; @@ -60,6 +62,7 @@ public class WylieEnglish implements TranscriptView catch (JDOMException jdome) { jdome.printStackTrace(); + ThdlDebug.noteIffyCode(); } } @@ -187,6 +190,7 @@ public class WylieEnglish implements TranscriptView catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } } diff --git a/source/org/thdl/savant/tib/package.html b/source/org/thdl/savant/tib/package.html new file mode 100644 index 0000000..4a95b9d --- /dev/null +++ b/source/org/thdl/savant/tib/package.html @@ -0,0 +1,23 @@ + + + + + + +Provides Savant with the power to display Tibetan language +text beside video or audio of Tibetan speakers. +

+Supports Wylie, English, and Tibetan. + + diff --git a/source/org/thdl/savant/ucuchi/All.java b/source/org/thdl/savant/ucuchi/All.java index c17768c..e8b8a83 100644 --- a/source/org/thdl/savant/ucuchi/All.java +++ b/source/org/thdl/savant/ucuchi/All.java @@ -29,6 +29,8 @@ import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import org.thdl.savant.*; +import org.thdl.util.ThdlDebug; + public class All implements TranscriptView { private JTextPane text = null; @@ -57,6 +59,7 @@ public class All implements TranscriptView catch (JDOMException jdome) { jdome.printStackTrace(); + ThdlDebug.noteIffyCode(); } } @@ -203,6 +206,7 @@ public class All implements TranscriptView catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } } diff --git a/source/org/thdl/savant/ucuchi/English.java b/source/org/thdl/savant/ucuchi/English.java index 5825369..3dc3fe9 100644 --- a/source/org/thdl/savant/ucuchi/English.java +++ b/source/org/thdl/savant/ucuchi/English.java @@ -29,6 +29,8 @@ import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import org.thdl.savant.*; +import org.thdl.util.ThdlDebug; + public class English implements TranscriptView { private JTextPane text = null; @@ -57,6 +59,7 @@ public class English implements TranscriptView catch (JDOMException jdome) { jdome.printStackTrace(); + ThdlDebug.noteIffyCode(); } } @@ -174,6 +177,7 @@ public class English implements TranscriptView catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } } diff --git a/source/org/thdl/savant/ucuchi/Quechua.java b/source/org/thdl/savant/ucuchi/Quechua.java index 0a3cb18..d54706c 100644 --- a/source/org/thdl/savant/ucuchi/Quechua.java +++ b/source/org/thdl/savant/ucuchi/Quechua.java @@ -29,6 +29,8 @@ import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import org.thdl.savant.*; +import org.thdl.util.ThdlDebug; + public class Quechua implements TranscriptView { private JTextPane text = null; @@ -57,6 +59,7 @@ public class Quechua implements TranscriptView catch (JDOMException jdome) { jdome.printStackTrace(); + ThdlDebug.noteIffyCode(); } } @@ -173,6 +176,7 @@ public class Quechua implements TranscriptView catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } } diff --git a/source/org/thdl/savant/ucuchi/QuechuaEnglish.java b/source/org/thdl/savant/ucuchi/QuechuaEnglish.java index c1191bc..9dddd0e 100644 --- a/source/org/thdl/savant/ucuchi/QuechuaEnglish.java +++ b/source/org/thdl/savant/ucuchi/QuechuaEnglish.java @@ -29,6 +29,8 @@ import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import org.thdl.savant.*; +import org.thdl.util.ThdlDebug; + public class QuechuaEnglish implements TranscriptView { private JTextPane text = null; @@ -57,6 +59,7 @@ public class QuechuaEnglish implements TranscriptView catch (JDOMException jdome) { jdome.printStackTrace(); + ThdlDebug.noteIffyCode(); } } @@ -195,6 +198,7 @@ public class QuechuaEnglish implements TranscriptView catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } } diff --git a/source/org/thdl/savant/ucuchi/SegmentedQuechua.java b/source/org/thdl/savant/ucuchi/SegmentedQuechua.java index cdaa07b..97c3f40 100644 --- a/source/org/thdl/savant/ucuchi/SegmentedQuechua.java +++ b/source/org/thdl/savant/ucuchi/SegmentedQuechua.java @@ -30,6 +30,8 @@ import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import org.thdl.savant.*; +import org.thdl.util.ThdlDebug; + public class SegmentedQuechua implements TranscriptView { private JTextPane text = null; @@ -58,6 +60,7 @@ public class SegmentedQuechua implements TranscriptView catch (JDOMException jdome) { jdome.printStackTrace(); + ThdlDebug.noteIffyCode(); } } @@ -195,6 +198,7 @@ public class SegmentedQuechua implements TranscriptView catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } } diff --git a/source/org/thdl/savant/ucuchi/package.html b/source/org/thdl/savant/ucuchi/package.html new file mode 100644 index 0000000..243f12b --- /dev/null +++ b/source/org/thdl/savant/ucuchi/package.html @@ -0,0 +1,23 @@ + + + + + + +Provides Savant with the power to display Quechua language +text beside video or audio of Quechua speakers. +

+Supports Quechua and English. + + diff --git a/source/org/thdl/tib/input/DuffPane.java b/source/org/thdl/tib/input/DuffPane.java index 60a367b..c55b6cc 100644 --- a/source/org/thdl/tib/input/DuffPane.java +++ b/source/org/thdl/tib/input/DuffPane.java @@ -32,6 +32,8 @@ import org.thdl.tib.text.*; import java.io.*; import javax.swing.text.rtf.*; +import org.thdl.util.ThdlDebug; + /** * Enables input of Tibetan text * using Tibetan Computer Company's free cross-platform TibetanMachineWeb fonts. @@ -764,6 +766,7 @@ class RTFSelection implements ClipboardOwner, Transferable { newDoc.insertString(i-offset, s, as); } catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } } rtfOut = new ByteArrayOutputStream(); @@ -771,8 +774,10 @@ class RTFSelection implements ClipboardOwner, Transferable { plainText = getText(offset, length); } catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } catch (IOException ioe) { ioe.printStackTrace(); + ThdlDebug.noteIffyCode(); } } public void lostOwnership(Clipboard clipboard, Transferable contents) { @@ -816,6 +821,7 @@ class RTFSelection implements ClipboardOwner, Transferable { rtfBoard.setContents(rtfSelection, rtfSelection); } catch (IllegalStateException ise) { ise.printStackTrace(); + ThdlDebug.noteIffyCode(); } } if (remove) @@ -823,6 +829,7 @@ class RTFSelection implements ClipboardOwner, Transferable { getDocument().remove(p1, p2-p1); } catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } } @@ -853,6 +860,7 @@ public void paste(int offset) { doc.insertString(p1+i, s, as); } catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } } } else if (contents.isDataFlavorSupported(DataFlavor.stringFlavor)) { @@ -861,12 +869,16 @@ public void paste(int offset) { } } catch (UnsupportedFlavorException ufe) { ufe.printStackTrace(); + ThdlDebug.noteIffyCode(); } catch (IOException ioe) { ioe.printStackTrace(); + ThdlDebug.noteIffyCode(); } catch (IllegalStateException ise) { ise.printStackTrace(); + ThdlDebug.noteIffyCode(); } catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } } @@ -1347,6 +1359,7 @@ public void paste(int offset) { } catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } } @@ -1368,6 +1381,7 @@ public void paste(int offset) { offset++; } catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } } else { TibetanDocument.DuffData[] dd = TibetanDocument.getTibetanMachineWeb(next); @@ -1444,6 +1458,7 @@ public void paste(int offset) { } catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } } diff --git a/source/org/thdl/tib/input/Jskad.java b/source/org/thdl/tib/input/Jskad.java index 92e4ca6..53cfe50 100644 --- a/source/org/thdl/tib/input/Jskad.java +++ b/source/org/thdl/tib/input/Jskad.java @@ -32,7 +32,9 @@ import javax.swing.text.*; import javax.swing.text.rtf.*; import org.thdl.tib.text.*; -import org.thdl.util.TeeStream; +import org.thdl.util.ThdlDebug; +import org.thdl.util.ThdlActionListener; + /** * A simple Tibetan text editor. Jskad editors lack most of the @@ -97,8 +99,8 @@ public class Jskad extends JPanel implements DocumentListener { JMenuItem newItem = new JMenuItem("New"); // newItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,2)); //Ctrl-n - newItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + newItem.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { newFile(); } }); @@ -106,8 +108,8 @@ public class Jskad extends JPanel implements DocumentListener { JMenuItem openItem = new JMenuItem("Open"); // openItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,2)); //Ctrl-o - openItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + openItem.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { openFile(); } }); @@ -115,8 +117,8 @@ public class Jskad extends JPanel implements DocumentListener { if (parentObject instanceof JFrame) { JMenuItem closeItem = new JMenuItem("Close"); - closeItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + closeItem.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { if (!hasChanged || hasChanged && checkSave()) { numberOfTibsRTFOpen--; @@ -134,8 +136,8 @@ public class Jskad extends JPanel implements DocumentListener { JMenuItem saveItem = new JMenuItem("Save"); // saveItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,2)); //Ctrl-s - saveItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + saveItem.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { if (fileName == null) saveAsFile(); else @@ -147,8 +149,8 @@ public class Jskad extends JPanel implements DocumentListener { fileMenu.add(saveItem); JMenuItem saveAsItem = new JMenuItem("Save as"); - saveAsItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + saveAsItem.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { saveAsFile(); } }); @@ -162,8 +164,8 @@ public class Jskad extends JPanel implements DocumentListener { if (parentObject instanceof JFrame || parentObject instanceof JInternalFrame) { JMenuItem cutItem = new JMenuItem("Cut"); cutItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X,2)); //Ctrl-x - cutItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + cutItem.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { cutSelection(); } }); @@ -171,8 +173,8 @@ public class Jskad extends JPanel implements DocumentListener { JMenuItem copyItem = new JMenuItem("Copy"); copyItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C,2)); //Ctrl-c - copyItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + copyItem.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { copySelection(); } }); @@ -180,8 +182,8 @@ public class Jskad extends JPanel implements DocumentListener { JMenuItem pasteItem = new JMenuItem("Paste"); pasteItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V,2)); //Ctrl-v - pasteItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + pasteItem.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { pasteSelection(); } }); @@ -190,8 +192,8 @@ public class Jskad extends JPanel implements DocumentListener { JMenuItem selectallItem = new JMenuItem("Select All"); selectallItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A,2)); //Ctrl-a - selectallItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + selectallItem.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { dp.setSelectionStart(0); dp.setSelectionEnd(dp.getDocument().getLength()); } @@ -200,8 +202,8 @@ public class Jskad extends JPanel implements DocumentListener { } JMenuItem preferencesItem = new JMenuItem("Preferences"); - preferencesItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + preferencesItem.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { getPreferences(); } }); @@ -213,16 +215,16 @@ public class Jskad extends JPanel implements DocumentListener { JMenu toolsMenu = new JMenu("Tools"); JMenuItem TMWWylieItem = new JMenuItem("Convert Tibetan to Wylie"); - TMWWylieItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + TMWWylieItem.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { toWylie(); } }); toolsMenu.add(TMWWylieItem); JMenuItem wylieTMWItem = new JMenuItem("Convert Wylie to Tibetan"); - wylieTMWItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + wylieTMWItem.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { toTibetan(); } }); @@ -230,8 +232,8 @@ public class Jskad extends JPanel implements DocumentListener { if (parentObject instanceof JFrame || parentObject instanceof JInternalFrame) { JMenuItem importItem = new JMenuItem("Import Wylie as Tibetan"); - importItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + importItem.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { importWylie(); } }); @@ -244,8 +246,8 @@ public class Jskad extends JPanel implements DocumentListener { JMenu infoMenu = new JMenu("Info"); JMenuItem aboutItem = new JMenuItem("About"); - aboutItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + aboutItem.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(Jskad.this, "Copyright 2001 Tibetan and Himalayan Digital Library\n"+ "Programmed by Edward Garrett\n\n"+ @@ -275,8 +277,8 @@ public class Jskad extends JPanel implements DocumentListener { String[] input_modes = {"Tibetan","Roman"}; final JComboBox inputmethods = new JComboBox(input_modes); - inputmethods.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + inputmethods.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { switch (inputmethods.getSelectedIndex()) { case 0: //Tibetan if (dp.isRomanMode()) @@ -300,8 +302,8 @@ public class Jskad extends JPanel implements DocumentListener { String[] keyboard_options = {"Extended Wylie","TCC Keyboard #1","TCC Keyboard #2","Sambhota Keymap One"}; final JComboBox keyboards = new JComboBox(keyboard_options); - keyboards.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { + keyboards.addActionListener(new ThdlActionListener() { + public void theRealActionPerformed(ActionEvent e) { switch (keyboards.getSelectedIndex()) { case 0: //Extended Wylie installKeyboard("wylie"); @@ -599,9 +601,11 @@ public class Jskad extends JPanel implements DocumentListener { hasChanged = false; } catch (IOException exception) { exception.printStackTrace(); + ThdlDebug.noteIffyCode(); return null; } catch (BadLocationException ble) { ble.printStackTrace(); + ThdlDebug.noteIffyCode(); } return f_name; } @@ -899,26 +903,12 @@ public class Jskad extends JPanel implements DocumentListener { } /** -* Runs Jskad. -* System output, including errors, is redirected to jskad.log. -* If you discover a bug, please send us an email, making sure -* to include the jskad.log file as an attachment. -*/ +* Runs Jskad. System output, including errors, is redirected to +* jskad.log in addition to appearing on the console as per usual. If +* you discover a bug, please send us an email, making sure to include +* the jskad.log file as an attachment. */ public static void main(String[] args) { - - try { - PrintStream psOut - = new TeeStream(System.out, - new PrintStream(new FileOutputStream("jskad.log"))); - PrintStream psErr - = new TeeStream(System.err, - new PrintStream(new FileOutputStream("jskad.log"))); - System.setErr(psErr); - System.setOut(psOut); - } - catch (Exception e) { - } - + ThdlDebug.attemptToSetUpLogFile("jskad.log"); try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); diff --git a/source/org/thdl/tib/input/Jskad2JavaScript.java b/source/org/thdl/tib/input/Jskad2JavaScript.java index d98e373..7b2f01b 100644 --- a/source/org/thdl/tib/input/Jskad2JavaScript.java +++ b/source/org/thdl/tib/input/Jskad2JavaScript.java @@ -22,9 +22,11 @@ import java.applet.Applet; import java.awt.*; import java.awt.event.*; import javax.swing.*; -import org.thdl.tib.text.*; import netscape.javascript.JSObject; +import org.thdl.tib.text.*; +import org.thdl.util.ThdlActionListener; + /** * A version of Jskad which can be embedded into * a web page as an applet. It includes all of the functionality @@ -54,9 +56,9 @@ public class Jskad2JavaScript extends JApplet { jskad = new Jskad(this); panel.add("Center", jskad); JButton submit = new JButton("Submit"); - submit.addActionListener(new ActionListener() + submit.addActionListener(new ThdlActionListener() { - public void actionPerformed(ActionEvent e) + public void theRealActionPerformed(ActionEvent e) { try { TibetanDocument t_doc = (TibetanDocument)jskad.dp.getDocument(); diff --git a/source/org/thdl/tib/input/JskadConversionTool.java b/source/org/thdl/tib/input/JskadConversionTool.java index 96f0c71..37f133f 100644 --- a/source/org/thdl/tib/input/JskadConversionTool.java +++ b/source/org/thdl/tib/input/JskadConversionTool.java @@ -22,9 +22,11 @@ import java.applet.Applet; import java.awt.*; import java.awt.event.*; import javax.swing.*; -import org.thdl.tib.text.*; import netscape.javascript.JSObject; +import org.thdl.tib.text.*; +import org.thdl.util.ThdlActionListener; + /** * A version of Jskad which can be embedded into * a web page as an applet. It includes all of the functionality @@ -54,9 +56,9 @@ public class JskadConversionTool extends JApplet { jskad = new Jskad(this); panel.add("Center", jskad); JButton submit = new JButton("Submit"); - submit.addActionListener(new ActionListener() + submit.addActionListener(new ThdlActionListener() { - public void actionPerformed(ActionEvent e) + public void theRealActionPerformed(ActionEvent e) { try { TibetanDocument t_doc = (TibetanDocument)jskad.dp.getDocument(); diff --git a/source/org/thdl/tib/input/package.html b/source/org/thdl/tib/input/package.html index b19eba2..3b0ff6b 100644 --- a/source/org/thdl/tib/input/package.html +++ b/source/org/thdl/tib/input/package.html @@ -1,35 +1,35 @@ - - - - - - -Provides classes and methods for inputting Tibetan text. -

-Designed for use with the Tibetan Computer -Company's free cross-platform TibetanMachineWeb fonts, this package -contains methods for inputting Tibetan using various keyboard -input methods, including true Wylie-based input, as well as -user-defined keyboards. -

-The package includes a simple Tibetan text editor, Jskad, -which can be run as an local application or embedded in a -web page. Jskad supports a wide range of functions, including -conversion back and forth between TibetanMachineWeb and -Extended Wylie. -

-

Related Documentation

-@see org.thdl.tib.text - - + + + + + + +Provides classes and methods for inputting Tibetan text. +

+Designed for use with the Tibetan Computer +Company's free cross-platform TibetanMachineWeb fonts, this package +contains methods for inputting Tibetan using various keyboard +input methods, including true Wylie-based input, as well as +user-defined keyboards. +

+The package includes a simple Tibetan text editor, Jskad, +which can be run as an local application or embedded in a +web page. Jskad supports a wide range of functions, including +conversion back and forth between TibetanMachineWeb and +Extended Wylie. +

+

Related Documentation

+@see org.thdl.tib.text + + diff --git a/source/org/thdl/tib/scanner/AboutDialog.java b/source/org/thdl/tib/scanner/AboutDialog.java index 6626b61..d125601 100644 --- a/source/org/thdl/tib/scanner/AboutDialog.java +++ b/source/org/thdl/tib/scanner/AboutDialog.java @@ -36,6 +36,9 @@ class AboutDialog extends Dialog implements ActionListener, WindowListener setSize(240,300); // the size ipaq's window. } + /* FIXME: what happens if this throws an exception? We'll just + see it on the console--it won't terminate the program. And the + user may not see the console! See ThdlActionListener. -DC */ public void actionPerformed(ActionEvent e) { hide(); @@ -116,4 +119,4 @@ class AboutDialog extends Dialog implements ActionListener, WindowListener public void windowOpened(WindowEvent e) { } -} \ No newline at end of file +} diff --git a/source/org/thdl/tib/scanner/AppletScannerFilter.java b/source/org/thdl/tib/scanner/AppletScannerFilter.java index 37a76ef..8249785 100644 --- a/source/org/thdl/tib/scanner/AppletScannerFilter.java +++ b/source/org/thdl/tib/scanner/AppletScannerFilter.java @@ -42,7 +42,7 @@ import org.thdl.tib.text.TibetanDocument; @author Andrés Montano Pellegrini @see RemoteScannerFilter - @see ScannerPannel + @see ScannerPanel */ public class AppletScannerFilter extends JApplet implements ActionListener, FocusListener, ItemListener { @@ -154,6 +154,9 @@ public class AppletScannerFilter extends JApplet implements ActionListener, Focu /* mnuEdit.setEnabled(false); objModified=null;*/ } + /* FIXME: what happens if this throws an exception? We'll just + see it on the console--it won't terminate the program. And the + user may not see the console! See ThdlActionListener. -DC */ public void actionPerformed(ActionEvent e) { Object clicked = e.getSource(); diff --git a/source/org/thdl/tib/scanner/DictionarySource.java b/source/org/thdl/tib/scanner/DictionarySource.java index 2c00c1a..1f5c3b3 100644 --- a/source/org/thdl/tib/scanner/DictionarySource.java +++ b/source/org/thdl/tib/scanner/DictionarySource.java @@ -28,7 +28,7 @@ public class DictionarySource { private int dicts; - /** Last bit of word. 1 if there are more brothers.*/ + /** Last bit of word; 1 if there are more brothers.*/ private static final int lastBit=32768; private static final int allDicts=lastBit-1; @@ -134,4 +134,4 @@ public class DictionarySource { dicts = 0; } -} \ No newline at end of file +} diff --git a/source/org/thdl/tib/scanner/FileSyllableListTree.java b/source/org/thdl/tib/scanner/FileSyllableListTree.java index 75a7728..6279b59 100644 --- a/source/org/thdl/tib/scanner/FileSyllableListTree.java +++ b/source/org/thdl/tib/scanner/FileSyllableListTree.java @@ -19,16 +19,16 @@ package org.thdl.tib.scanner; import java.io.*; -/** Searches the words directly in a file; not the prefered +/** Searches the words directly in a file; not the preferred implementation. The search is too slow! - The prefered implementation is the CachedSyllableList. + The preferred implementation is the CachedSyllableListTree.

The words must be stored in a binary file tree structure format. This can be done using the BinaryFileGenerator.

@author Andrés Montano Pellegrini @see TibetanScanner - @see CachedSyllableList + @see CachedSyllableListTree @see BinaryFileGenerator */ @@ -158,4 +158,4 @@ public class FileSyllableListTree implements SyllableListTree } return null; } -} \ No newline at end of file +} diff --git a/source/org/thdl/tib/scanner/ScannerPanel.java b/source/org/thdl/tib/scanner/ScannerPanel.java index f5801a0..7717c04 100644 --- a/source/org/thdl/tib/scanner/ScannerPanel.java +++ b/source/org/thdl/tib/scanner/ScannerPanel.java @@ -167,6 +167,9 @@ public abstract class ScannerPanel extends Panel implements ActionListener } } + /* FIXME: what happens if this throws an exception? We'll just + see it on the console--it won't terminate the program. And the + user may not see the console! See ThdlActionListener. -DC */ public void actionPerformed(ActionEvent e) { translate(); @@ -176,4 +179,4 @@ public abstract class ScannerPanel extends Panel implements ActionListener public abstract void clear(); public void setEnableTibetanScript(boolean enabled) {} public void addFocusListener(FocusListener fl) {} -} \ No newline at end of file +} diff --git a/source/org/thdl/tib/scanner/WindowScannerFilter.java b/source/org/thdl/tib/scanner/WindowScannerFilter.java index 6b243e4..44ba1f2 100644 --- a/source/org/thdl/tib/scanner/WindowScannerFilter.java +++ b/source/org/thdl/tib/scanner/WindowScannerFilter.java @@ -246,7 +246,10 @@ public class WindowScannerFilter implements WindowListener, FocusListener, Actio mnuDelete.setEnabled(false); mnuSelectAll.setEnabled(false); } - + + /* FIXME: what happens if this throws an exception? We'll just + see it on the console--it won't terminate the program. And the + user may not see the console! See ThdlActionListener. -DC */ public void actionPerformed(ActionEvent e) { Object clicked = e.getSource(); diff --git a/source/org/thdl/tib/scanner/package.html b/source/org/thdl/tib/scanner/package.html new file mode 100644 index 0000000..bf7b40e --- /dev/null +++ b/source/org/thdl/tib/scanner/package.html @@ -0,0 +1,21 @@ + + + + + + +Provides classes and methods for translating Tibetan text to English. +

+Right now, this package scans Tibetan text, but we aim to make it parse Tibetan text. +

+Author: Andrés Montano Pellegrini +

+

Related Documentation

+@see org.thdl.tib.text +@see org.thdl.tib.input + + diff --git a/source/org/thdl/tib/text/TibetanDocument.java b/source/org/thdl/tib/text/TibetanDocument.java index 94b274e..aaf1115 100644 --- a/source/org/thdl/tib/text/TibetanDocument.java +++ b/source/org/thdl/tib/text/TibetanDocument.java @@ -1,1264 +1,1295 @@ -/* -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.tib.text; - -import java.util.*; -import javax.swing.*; -import javax.swing.text.*; -import javax.swing.text.rtf.RTFEditorKit; -import java.io.*; - -/** -* Provides methods for converting back and forth between -* Extended Wylie and TibetanMachineWeb, and for inserting Tibetan -* into a styled document. -*

-* The class provides a variety of static methods for -* converting back and forth between Extended Wylie and TibetanMachineWeb. -* The Wylie can be accessed as a String, while the TibetanMachineWeb -* can be exported as Rich Text Format. -*

-* When instantiated as an instance object, -* this class also provides methods for inserting Tibetan -* (as TibetanMachineWeb) into a styled document. -* @author Edward Garrett, Tibetan and Himalayan Digital Library -* @version 1.0 -*/ -public class TibetanDocument extends DefaultStyledDocument { - private static List chars = new ArrayList(); - private int tibetanFontSize = 36; - -/** -* Creates a TibetanDocument. -* @param styles a StyleContext, which is simply passed on -* to DefaultStyledDocument's constructor -*/ - public TibetanDocument(StyleContext styles) { - super(styles); - } - -/** -* Sets the point size used by default for Tibetan text. -* @param size the point size for Tibetan text -*/ - public void setTibetanFontSize(int size) { - tibetanFontSize = size; - } - -/** -* Gets the point size for Tibetan text. -* @return the point size used for Tibetan text -*/ - public int getTibetanFontSize() { - return tibetanFontSize; - } - -/** -* Writes the document to an OutputStream as Rich Text Format (.rtf). -* @param out the OutputStream to write to -*/ - public void writeRTFOutputStream(OutputStream out) throws IOException { - RTFEditorKit rtf = new RTFEditorKit(); - - try { - rtf.write(out, this, 0, getLength()); - } - catch (BadLocationException ble) { - } - } - -/** -* Inserts Tibetan text into the document. The font size is applied automatically, -* according to the current Tibetan font size. -* @param offset the position at which you want to insert text -* @param s the string you want to insert -* @param attr the attributes to apply, normally a particular TibetanMachineWeb font -* @see #setTibetanFontSize(int size) -*/ - public void appendDuff(int offset, String s, MutableAttributeSet attr) { - try { - StyleConstants.setFontSize(attr, tibetanFontSize); - insertString(offset, s, attr); - } - catch (BadLocationException ble) { - } - } - -/** -* Inserts a stretch of TibetanMachineWeb data into the document. -* @param glyphs the array of Tibetan data you want to insert -* @param pos the position at which you want to insert text -*/ - public void insertDuff(int pos, DuffData[] glyphs) { - if (glyphs == null) - return; - - MutableAttributeSet mas; - for (int i=0; i -* 1 - TibetanMachineWeb
-* 2 - TibetanMachineWeb1
-* ...
-* 10 - TibetanMachineWeb9
-*

-* The string represents a contiguous stretch of data in that -* font, i.e. a stretch of TibetanMachineWeb that doesn't require a font change. -*/ - public static class DuffData { -/** -* a string of text -*/ - public String text; -/** -* the font number for this text -*/ - public int font; - -/** -* @param s a string of TibetanMachineWeb text -* @param i a TibetanMachineWeb font number -*/ - public DuffData(String s, int i) { - text = s; - font = i; - } - } - -/** -* Converts a list of glyphs into an array of {@link TibetanDocument.DuffData DuffData}. -* The motivation for this is that most processes - for example using -* TibetanMachineWeb in HTML - only need to know what -* text to output, and when to change fonts. In general, they don't -* need to have an explicit indication for each glyph of the font -* for that glyph. -* @param glyphs the list of TibetanMachineWeb glyphs -* you want to convert -* @return an array of DuffData corresponding to this -* list of glyphs -*/ - public static DuffData[] convertGlyphs(List glyphs) { - if (glyphs.size() == 0) - return null; - List data = new ArrayList(); - StringBuffer sb = new StringBuffer(); - Iterator iter = glyphs.iterator(); - DuffCode dc = (DuffCode)iter.next(); - int lastfont = dc.fontNum; - sb.append(dc.character); - - while (iter.hasNext()) { - dc = (DuffCode)iter.next(); - if (dc.fontNum == lastfont) - sb.append(dc.character); - else { - data.add(new DuffData(sb.toString(), lastfont)); - lastfont = dc.fontNum; - sb = new StringBuffer(); - sb.append(dc.character); - } - } - - data.add(new DuffData(sb.toString(), lastfont)); - - DuffData[] dd = new DuffData[0]; - dd = (DuffData[])data.toArray(dd); - return dd; - } - -/** -* Figures out how to arrange a list of characters into glyphs. For example, if the user types 'bsgr' -* using the Extended Wylie keyboard, this method figures out that this should be represented -* as a 'b' glyph followed by a 's-g-r' glyph. If you know that the characters do not -* contain Sanskrit stacks, or do not contain Tibetan stacks, then you can specify this -* to speed the process up. Otherwise, the method will first check to see if the characters -* correspond to any Tibetan stacks, and if not, then it will check for Sanskrit stacks. -* @param chars the list of Tibetan characters you want to find glyphs for -* @param areStacksOnRight whether stacking should try to maximize from right to left (true) -* or from left to right (false). In the Extended Wylie keyboard, you try to stack from -* right to left. Thus, the character sequence r-g-r would be stacked as r followed by gr, -* rather than rg followed by r. In the Sambhota and TCC keyboards, the stack direction -* is reversed. -* @param definitelyTibetan should be true if the characters are known to be Tibetan and -* not Sanskrit -* @param definitelySanskrit should be true if the characters are known to be Sanskrit and -* not Tibetan -*/ - public static List getGlyphs(List chars, boolean areStacksOnRight, boolean definitelyTibetan, boolean definitelySanskrit) { - StringBuffer tibBuffer, sanBuffer; - String tibCluster, sanCluster; - - boolean checkTibetan, checkSanskrit; - - if (!(definitelyTibetan || definitelySanskrit)) { - checkTibetan = true; - checkSanskrit = true; - } - else { - checkTibetan = definitelyTibetan; - checkSanskrit = definitelySanskrit; - } - - int length = chars.size(); - - List glyphs = new ArrayList(); - glyphs.clear(); - - if (areStacksOnRight) { - for (int i=0; i-1; i--) { - tibBuffer = new StringBuffer(); - tibCluster = null; - - sanBuffer = new StringBuffer(); - sanCluster = null; - - Iterator iter = chars.iterator(); - - for (int k=0; k 1) { - dc = (DuffCode)glyphs.get(glyphs.size()-1); - if (!TibetanMachineWeb.isWyliePunc(TibetanMachineWeb.getWylieForGlyph(dc))) { - DuffCode dc_2 = (DuffCode)glyphs.removeLast(); - DuffCode dc_1 = (DuffCode)glyphs.removeLast(); - glyphs.addAll(getVowel(dc_1, dc_2, next)); - break vowel_block; - } - } - DuffCode[] dc_array = (DuffCode[])TibetanMachineWeb.getTibHash().get(TibetanMachineWeb.ACHEN); - dc = dc_array[TibetanMachineWeb.TMW]; - glyphs.addAll(getVowel(dc, next)); - } - - chars.clear(); - } - - isSanskrit = false; - } - - else if (TibetanMachineWeb.isWylieChar(next)) { - if (!isSanskrit) //add char to list - it is not sanskrit - chars.add(next); - - else if (wasLastSanskritStackingKey) { //add char to list - it is still part of sanskrit stack - chars.add(next); - wasLastSanskritStackingKey = false; - } - - else { //char is no longer part of sanskrit stack, therefore compute and add previous stack - glyphs.addAll(getGlyphs(chars, true, !isSanskrit, isSanskrit)); - chars.clear(); - chars.add(next); - isSanskrit = false; - wasLastSanskritStackingKey = false; - } - } - - else if (next.equals(String.valueOf(TibetanMachineWeb.WYLIE_DISAMBIGUATING_KEY))) { - if (!chars.isEmpty()) - glyphs.addAll(getGlyphs(chars, true, !isSanskrit, isSanskrit)); - - chars.clear(); - isSanskrit = false; - } - - else if (next.equals(String.valueOf(TibetanMachineWeb.WYLIE_SANSKRIT_STACKING_KEY))) { - if (!isSanskrit) { //begin sanskrit stack - switch (chars.size()) { - case 0: - break; //'+' is not "pre-stacking" key - - case 1: - isSanskrit = true; - wasLastSanskritStackingKey = true; - break; - - default: - String top_char = (String)chars.get(chars.size()-1); - chars.remove(chars.size()-1); - glyphs.addAll(getGlyphs(chars, true, !isSanskrit, isSanskrit)); - chars.clear(); - chars.add(top_char); - isSanskrit = true; - wasLastSanskritStackingKey = true; - break; - } - } - } - - else if (TibetanMachineWeb.isFormatting(next.charAt(0))) { - if (!chars.isEmpty()) - glyphs.addAll(getGlyphs(chars, true, !isSanskrit, isSanskrit)); - - dc = new DuffCode(1,next.charAt(0)); - glyphs.add(dc); - chars.clear(); - isSanskrit = false; - } - - if (next != null) - start += next.length(); - } - - if (!chars.isEmpty()) { - glyphs.addAll(getGlyphs(chars, true, !isSanskrit, isSanskrit)); - chars.clear(); - } - - DuffData[] dd = convertGlyphs(glyphs); - return dd; - } - -/** -* Gets the bindu sequence for a given context. -* In the TibetanMachineWeb fonts, bindu (anusvara) is realized -* differently depending on which vowel it attaches to. Although -* the default bindu glyph is affixed to consonants and subscript vowels, -* for superscript vowels (i, e, o, etc), there is a single glyph -* which merges the bindu and that vowel together. When you pass this -* method a glyph context, it will return a List of glyphs which -* will either consist of the original glyph followed by the default -* bindu glyph, or a composite vowel+bindu glyph. -* Note that there is only one glyph in the context. This means that -* bindus will not affix properly if superscript vowels are allowed to directly -* precede subscript vowels (e.g. pou). -* @param dc the DuffCode of the glyph you -* want to attach a bindu to -* @return a List of DuffCode glyphs that include the -* original dc, as well as a bindu -*/ - public static List getBindu(DuffCode dc) { - List bindus = new ArrayList(); - - if (null == dc) { - bindus.add(TibetanMachineWeb.getGlyph(String.valueOf(TibetanMachineWeb.BINDU))); - return bindus; - } - - if (!TibetanMachineWeb.getBinduMap().containsKey(dc)) { - bindus.add(dc); - bindus.add(TibetanMachineWeb.getGlyph(String.valueOf(TibetanMachineWeb.BINDU))); - return bindus; - } - - bindus.add((DuffCode)TibetanMachineWeb.getBinduMap().get(dc)); - return bindus; - } - -/** -* Gets the vowel sequence for a given vowel in a given context. -* Given a context, this method affixes a vowel and returns the -* context plus the vowel. Generally, it is enough to provide just -* one glyph for context. -* @param context the glyph preceding the vowel you want to affix -* @param vowel the vowel you want to affix, in Wylie -* @return a List of glyphs equal to the vowel in context -*/ - public static List getVowel(DuffCode context, String vowel) { - return getVowel(null, context, vowel); - } - -/** -* Gets the vowel sequence for a given vowel in a given context. -* Given a context, this method affixes a vowel and returns the context plus the vowel. -* Since the choice of vowel glyph depends on the consonant to which it is attached, -* generally it is enough to provide just the immediately preceding context. However, -* in some cases, double vowels are allowed - for example 'buo'. To find the correct -* glyph for 'o', we need 'b' in this case, not 'u'. Note also that some Extended -* Wylie vowels correspond to multiple glyphs in TibetanMachineWeb. For example, -* the vowel I consists of both an achung and a reverse gigu. All required glyphs -* are part of the returned List. -* @param context_1 the glyph occurring two glyphs before the vowel you want to affix -* @param context_2 the glyph immediately before the vowel you want to affix -* @param vowel the vowel you want to affix, in Wylie -* @return a List of glyphs equal to the vowel in context -*/ - - public static List getVowel(DuffCode context_1, DuffCode context_2, String vowel) { - List vowels = new ArrayList(); - -//this vowel doesn't correspond to a glyph - -//so you just return the original context - - if ( vowel.equals(TibetanMachineWeb.WYLIE_aVOWEL) || - TibetanMachineWeb.isTopVowel(context_2)) { - if (context_1 != null) - vowels.add(context_1); - - vowels.add(context_2); - return vowels; - } - -//first, the three easiest cases: ai, au, and 0 || !glyphList.isEmpty()) { - if (needsVowel) - wylieBuffer.append(withA(glyphList)); - else - wylieBuffer.append(withoutA(glyphList)); - - glyphList.clear(); - needsVowel = true; - isLastVowel = false; - } - - wylieBuffer.append(ch); - } - else { - wylie = TibetanMachineWeb.getWylieForGlyph(dcs[i]); - - boolean containsBindu = false; - if (wylie.length() > 1 && wylie.charAt(wylie.length()-1) == TibetanMachineWeb.BINDU) { - char[] cArray = wylie.toCharArray(); - wylie = new String(cArray, 0, wylie.length()-1); - containsBindu = true; - } - - process_block: { - if (TibetanMachineWeb.isWyliePunc(wylie)) { - isLastVowel = false; - - if (glyphList.isEmpty()) - wylieBuffer.append(wylie); - - else { - if (needsVowel) - wylieBuffer.append(withA(glyphList)); - else - wylieBuffer.append(withoutA(glyphList)); - - wylieBuffer.append(wylie); //append the punctuation - - glyphList.clear(); - } - needsVowel = true; //next consonants are syllable onset, so we are awaiting vowel - } - - //isChar must come before isVowel because ACHEN has priority over WYLIE_aVOWEL - else if (TibetanMachineWeb.isWylieChar(wylie)) { - isLastVowel = false; - glyphList.add(dcs[i]); - } - - else if (TibetanMachineWeb.isWylieVowel(wylie)) { - if (isLastVowel) { - int len = wylieBuffer.length(); - int A_len = TibetanMachineWeb.A_VOWEL.length(); - - if (wylieBuffer.substring(len-A_len).equals(TibetanMachineWeb.A_VOWEL)) { - try { - if (wylie.equals(TibetanMachineWeb.i_VOWEL)) { - wylieBuffer.delete(len-A_len, len); - wylieBuffer.append(TibetanMachineWeb.I_VOWEL); - isLastVowel = false; - break process_block; - } - else if (wylie.equals(TibetanMachineWeb.reverse_i_VOWEL)) { - wylieBuffer.delete(len-A_len, len); - wylieBuffer.append(TibetanMachineWeb.reverse_I_VOWEL); - isLastVowel = false; - break process_block; - } - } - catch (StringIndexOutOfBoundsException se) { - } - - wylieBuffer.append(wylie); //append current vowel - isLastVowel = false; - } - else - wylieBuffer.append(wylie); //append current vowel - } - else { - int glyphCount = glyphList.size(); - boolean insertDisAmbig = false; - - if (0 != glyphCount) { - DuffCode top_dc = (DuffCode)glyphList.get(glyphCount-1); - String top_wylie = TibetanMachineWeb.getWylieForGlyph(top_dc); - - if (top_wylie.equals(TibetanMachineWeb.ACHEN)) { - glyphList.remove(glyphCount-1); - - if (glyphCount-1 == 0) - top_dc = null; - else { - insertDisAmbig = true; - top_dc = (DuffCode)glyphList.get(glyphCount-2); - } - } - - if (top_dc == null || !TibetanMachineWeb.getWylieForGlyph(top_dc).equals(TibetanMachineWeb.ACHUNG)) - wylieBuffer.append(withoutA(glyphList)); //append consonants in glyphList - else { - glyphCount = glyphList.size(); - glyphList.remove(glyphCount-1); - - if (glyphCount-1 != 0) - wylieBuffer.append(withA(glyphList)); - - wylieBuffer.append(TibetanMachineWeb.ACHUNG); - } - } - - if (insertDisAmbig) - wylieBuffer.append(TibetanMachineWeb.WYLIE_DISAMBIGUATING_KEY); - - wylieBuffer.append(wylie); //append vowel - - glyphList.clear(); - isLastVowel = true; - needsVowel = false; - } - } - else { //must be a stack - isLastVowel = false; - glyphList.add(dcs[i]); - } - } - - if (containsBindu) { - isLastVowel = false; - wylieBuffer.append(withoutA(glyphList)); - wylieBuffer.append(TibetanMachineWeb.BINDU); //append the bindu - glyphList.clear(); - } - } - } - - //replace TMW with Wylie - - if (!glyphList.isEmpty()) { - if (needsVowel) - wylieBuffer.append(withA(glyphList)); - else - wylieBuffer.append(withoutA(glyphList)); - } - - if (wylieBuffer.length() > 0) - return wylieBuffer.toString(); - else - return null; - } -} \ No newline at end of file +/* +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.tib.text; + +import java.util.*; +import javax.swing.*; +import javax.swing.text.*; +import javax.swing.text.rtf.RTFEditorKit; +import java.io.*; + +import org.thdl.util.ThdlDebug; + +/** +* Provides methods for converting back and forth between +* Extended Wylie and TibetanMachineWeb, and for inserting Tibetan +* into a styled document. +*

+* The class provides a variety of static methods for +* converting back and forth between Extended Wylie and TibetanMachineWeb. +* The Wylie can be accessed as a String, while the TibetanMachineWeb +* can be exported as Rich Text Format. +*

+* When instantiated as an instance object, +* this class also provides methods for inserting Tibetan +* (as TibetanMachineWeb) into a styled document. +* @author Edward Garrett, Tibetan and Himalayan Digital Library +* @version 1.0 +*/ +public class TibetanDocument extends DefaultStyledDocument { + private static List chars = new ArrayList(); + private int tibetanFontSize = 36; + +/** +* Creates a TibetanDocument. +* @param styles a StyleContext, which is simply passed on +* to DefaultStyledDocument's constructor +*/ + public TibetanDocument(StyleContext styles) { + super(styles); + } + +/** +* Sets the point size used by default for Tibetan text. +* @param size the point size for Tibetan text +*/ + public void setTibetanFontSize(int size) { + tibetanFontSize = size; + } + +/** +* Gets the point size for Tibetan text. +* @return the point size used for Tibetan text +*/ + public int getTibetanFontSize() { + return tibetanFontSize; + } + +/** +* Writes the document to an OutputStream as Rich Text Format (.rtf). +* @param out the OutputStream to write to +*/ + public void writeRTFOutputStream(OutputStream out) throws IOException { + RTFEditorKit rtf = new RTFEditorKit(); + + try { + rtf.write(out, this, 0, getLength()); + } + catch (BadLocationException ble) { + } + } + +/** +* Inserts Tibetan text into the document. The font size is applied automatically, +* according to the current Tibetan font size. +* @param offset the position at which you want to insert text +* @param s the string you want to insert +* @param attr the attributes to apply, normally a particular TibetanMachineWeb font +* @see #setTibetanFontSize(int size) +*/ + public void appendDuff(int offset, String s, MutableAttributeSet attr) { + try { + StyleConstants.setFontSize(attr, tibetanFontSize); + insertString(offset, s, attr); + } + catch (BadLocationException ble) { + } + } + +/** +* Inserts a stretch of TibetanMachineWeb data into the document. +* @param glyphs the array of Tibetan data you want to insert +* @param pos the position at which you want to insert text +*/ + public int insertDuff(int pos, DuffData[] glyphs) { + if (glyphs == null) + return pos; + + MutableAttributeSet mas; + for (int i=0; i +* 1 - TibetanMachineWeb
+* 2 - TibetanMachineWeb1
+* ...
+* 10 - TibetanMachineWeb9
+*

+* The string represents a contiguous stretch of data in that +* font, i.e. a stretch of TibetanMachineWeb that doesn't require a font change. +*/ + public static class DuffData { +/** +* a string of text +*/ + public String text; +/** +* the font number for this text +*/ + public int font; + +/** +* @param s a string of TibetanMachineWeb text +* @param i a TibetanMachineWeb font number +*/ + public DuffData(String s, int i) { + text = s; + font = i; + } + } + +/** +* Converts a list of glyphs into an array of {@link TibetanDocument.DuffData DuffData}. +* The motivation for this is that most processes - for example using +* TibetanMachineWeb in HTML - only need to know what +* text to output, and when to change fonts. In general, they don't +* need to have an explicit indication for each glyph of the font +* for that glyph. +* @param glyphs the list of TibetanMachineWeb glyphs +* you want to convert +* @return an array of DuffData corresponding to this +* list of glyphs +*/ + public static DuffData[] convertGlyphs(List glyphs) { + if (glyphs.size() == 0) + return null; + List data = new ArrayList(); + StringBuffer sb = new StringBuffer(); + Iterator iter = glyphs.iterator(); + DuffCode dc = (DuffCode)iter.next(); + int lastfont = dc.fontNum; + sb.append(dc.character); + + while (iter.hasNext()) { + dc = (DuffCode)iter.next(); + if (dc.fontNum == lastfont) + sb.append(dc.character); + else { + data.add(new DuffData(sb.toString(), lastfont)); + lastfont = dc.fontNum; + sb = new StringBuffer(); + sb.append(dc.character); + } + } + + data.add(new DuffData(sb.toString(), lastfont)); + + DuffData[] dd = new DuffData[0]; + dd = (DuffData[])data.toArray(dd); + return dd; + } + +/** +* Figures out how to arrange a list of characters into glyphs. For example, if the user types 'bsgr' +* using the Extended Wylie keyboard, this method figures out that this should be represented +* as a 'b' glyph followed by a 's-g-r' glyph. If you know that the characters do not +* contain Sanskrit stacks, or do not contain Tibetan stacks, then you can specify this +* to speed the process up. Otherwise, the method will first check to see if the characters +* correspond to any Tibetan stacks, and if not, then it will check for Sanskrit stacks. +* @param chars the list of Tibetan characters you want to find glyphs for +* @param areStacksOnRight whether stacking should try to maximize from right to left (true) +* or from left to right (false). In the Extended Wylie keyboard, you try to stack from +* right to left. Thus, the character sequence r-g-r would be stacked as r followed by gr, +* rather than rg followed by r. In the Sambhota and TCC keyboards, the stack direction +* is reversed. +* @param definitelyTibetan should be true if the characters are known to be Tibetan and +* not Sanskrit +* @param definitelySanskrit should be true if the characters are known to be Sanskrit and +* not Tibetan +*/ + public static List getGlyphs(List chars, boolean areStacksOnRight, boolean definitelyTibetan, boolean definitelySanskrit) { + StringBuffer tibBuffer, sanBuffer; + String tibCluster, sanCluster; + + boolean checkTibetan, checkSanskrit; + + if (!(definitelyTibetan || definitelySanskrit)) { + checkTibetan = true; + checkSanskrit = true; + } + else { + checkTibetan = definitelyTibetan; + checkSanskrit = definitelySanskrit; + } + + int length = chars.size(); + + List glyphs = new ArrayList(); + glyphs.clear(); + + if (areStacksOnRight) { + for (int i=0; i-1; i--) { + tibBuffer = new StringBuffer(); + tibCluster = null; + + sanBuffer = new StringBuffer(); + sanCluster = null; + + Iterator iter = chars.iterator(); + + for (int k=0; k 1) { + dc = (DuffCode)glyphs.get(glyphs.size()-1); + if (!TibetanMachineWeb.isWyliePunc(TibetanMachineWeb.getWylieForGlyph(dc))) { + DuffCode dc_2 = (DuffCode)glyphs.removeLast(); + DuffCode dc_1 = (DuffCode)glyphs.removeLast(); + glyphs.addAll(getVowel(dc_1, dc_2, next)); + break vowel_block; + } + } + DuffCode[] dc_array = (DuffCode[])TibetanMachineWeb.getTibHash().get(TibetanMachineWeb.ACHEN); + dc = dc_array[TibetanMachineWeb.TMW]; + glyphs.addAll(getVowel(dc, next)); + } + + chars.clear(); + } + + isSanskrit = false; + } + + else if (TibetanMachineWeb.isWylieChar(next)) { + if (!isSanskrit) //add char to list - it is not sanskrit + chars.add(next); + + else if (wasLastSanskritStackingKey) { //add char to list - it is still part of sanskrit stack + chars.add(next); + wasLastSanskritStackingKey = false; + } + + else { //char is no longer part of sanskrit stack, therefore compute and add previous stack + glyphs.addAll(getGlyphs(chars, true, !isSanskrit, isSanskrit)); + chars.clear(); + chars.add(next); + isSanskrit = false; + wasLastSanskritStackingKey = false; + } + } + + else if (next.equals(String.valueOf(TibetanMachineWeb.WYLIE_DISAMBIGUATING_KEY))) { + if (!chars.isEmpty()) + glyphs.addAll(getGlyphs(chars, true, !isSanskrit, isSanskrit)); + + chars.clear(); + isSanskrit = false; + } + + else if (next.equals(String.valueOf(TibetanMachineWeb.WYLIE_SANSKRIT_STACKING_KEY))) { + if (!isSanskrit) { //begin sanskrit stack + switch (chars.size()) { + case 0: + break; //'+' is not "pre-stacking" key + + case 1: + isSanskrit = true; + wasLastSanskritStackingKey = true; + break; + + default: + String top_char = (String)chars.get(chars.size()-1); + chars.remove(chars.size()-1); + glyphs.addAll(getGlyphs(chars, true, !isSanskrit, isSanskrit)); + chars.clear(); + chars.add(top_char); + isSanskrit = true; + wasLastSanskritStackingKey = true; + break; + } + } + } + + else if (TibetanMachineWeb.isFormatting(next.charAt(0))) { + if (!chars.isEmpty()) + glyphs.addAll(getGlyphs(chars, true, !isSanskrit, isSanskrit)); + + dc = new DuffCode(1,next.charAt(0)); + glyphs.add(dc); + chars.clear(); + isSanskrit = false; + } + + if (next != null) + start += next.length(); + } + + if (!chars.isEmpty()) { + glyphs.addAll(getGlyphs(chars, true, !isSanskrit, isSanskrit)); + chars.clear(); + } + + DuffData[] dd = convertGlyphs(glyphs); + return dd; + } + +/** +* Gets the bindu sequence for a given context. +* In the TibetanMachineWeb fonts, bindu (anusvara) is realized +* differently depending on which vowel it attaches to. Although +* the default bindu glyph is affixed to consonants and subscript vowels, +* for superscript vowels (i, e, o, etc), there is a single glyph +* which merges the bindu and that vowel together. When you pass this +* method a glyph context, it will return a List of glyphs which +* will either consist of the original glyph followed by the default +* bindu glyph, or a composite vowel+bindu glyph. +* Note that there is only one glyph in the context. This means that +* bindus will not affix properly if superscript vowels are allowed to directly +* precede subscript vowels (e.g. pou). +* @param dc the DuffCode of the glyph you +* want to attach a bindu to +* @return a List of DuffCode glyphs that include the +* original dc, as well as a bindu +*/ + public static List getBindu(DuffCode dc) { + List bindus = new ArrayList(); + + if (null == dc) { + bindus.add(TibetanMachineWeb.getGlyph(String.valueOf(TibetanMachineWeb.BINDU))); + return bindus; + } + + if (!TibetanMachineWeb.getBinduMap().containsKey(dc)) { + bindus.add(dc); + bindus.add(TibetanMachineWeb.getGlyph(String.valueOf(TibetanMachineWeb.BINDU))); + return bindus; + } + + bindus.add((DuffCode)TibetanMachineWeb.getBinduMap().get(dc)); + return bindus; + } + +/** +* Gets the vowel sequence for a given vowel in a given context. +* Given a context, this method affixes a vowel and returns the +* context plus the vowel. Generally, it is enough to provide just +* one glyph for context. +* @param context the glyph preceding the vowel you want to affix +* @param vowel the vowel you want to affix, in Wylie +* @return a List of glyphs equal to the vowel in context +*/ + public static List getVowel(DuffCode context, String vowel) { + return getVowel(null, context, vowel); + } + +/** +* Gets the vowel sequence for a given vowel in a given context. +* Given a context, this method affixes a vowel and returns the context plus the vowel. +* Since the choice of vowel glyph depends on the consonant to which it is attached, +* generally it is enough to provide just the immediately preceding context. However, +* in some cases, double vowels are allowed - for example 'buo'. To find the correct +* glyph for 'o', we need 'b' in this case, not 'u'. Note also that some Extended +* Wylie vowels correspond to multiple glyphs in TibetanMachineWeb. For example, +* the vowel I consists of both an achung and a reverse gigu. All required glyphs +* are part of the returned List. +* @param context_1 the glyph occurring two glyphs before the vowel you want to affix +* @param context_2 the glyph immediately before the vowel you want to affix +* @param vowel the vowel you want to affix, in Wylie +* @return a List of glyphs equal to the vowel in context +*/ + + public static List getVowel(DuffCode context_1, DuffCode context_2, String vowel) { + List vowels = new ArrayList(); + +//this vowel doesn't correspond to a glyph - +//so you just return the original context + + if ( vowel.equals(TibetanMachineWeb.WYLIE_aVOWEL) || + TibetanMachineWeb.isTopVowel(context_2)) { + if (context_1 != null) + vowels.add(context_1); + + vowels.add(context_2); + return vowels; + } + +//first, the three easiest cases: ai, au, and = end) + return ""; + + java.util.List dcs = new ArrayList(); + int i = begin; + StringBuffer wylieBuffer = new StringBuffer(); + + try { + while (i < end) { + attr = getCharacterElement(i).getAttributes(); + fontName = StyleConstants.getFontFamily(attr); + + ch = getText(i,1).charAt(0); + + //current character is formatting + if (ch == '\n' || ch == '\t') { + if (dcs.size() > 0) { + DuffCode[] dc_array = new DuffCode[0]; + dc_array = (DuffCode[])dcs.toArray(dc_array); + wylieBuffer.append(TibetanDocument.getWylie(dc_array)); + dcs.clear(); + } + wylieBuffer.append(ch); + } + + //current character isn't TMW + else if ((0 == (fontNum = TibetanMachineWeb.getTMWFontNumber(fontName)))) { + if (dcs.size() > 0) { + DuffCode[] dc_array = new DuffCode[0]; + dc_array = (DuffCode[])dcs.toArray(dc_array); + wylieBuffer.append(TibetanDocument.getWylie(dc_array)); + dcs.clear(); + } + } + + //current character is convertable + else { + dc = new DuffCode(fontNum, ch); + dcs.add(dc); + } + i++; + } + if (dcs.size() > 0) { + DuffCode[] dc_array = new DuffCode[0]; + dc_array = (DuffCode[])dcs.toArray(dc_array); + wylieBuffer.append(TibetanDocument.getWylie(dc_array)); + } + return wylieBuffer.toString(); + } + catch (BadLocationException ble) { + ble.printStackTrace(); + ThdlDebug.noteIffyCode(); + } + + return ""; + } + +/** +* Gets the Extended Wylie for a set of glyphs. +* @param dcs an array of glyphs +* @return the Extended Wylie corresponding to these glyphs +*/ + public static String getWylie(DuffCode[] dcs) { + if (dcs.length == 0) + return null; + + AttributeSet attr; + String fontName; + int fontNum; + char ch; + String wylie; + + List glyphList = new ArrayList(); + boolean needsVowel = true; + boolean isLastVowel = false; + int start = 0; + StringBuffer wylieBuffer = new StringBuffer(); + + for (int i=start; i 0 || !glyphList.isEmpty()) { + if (needsVowel) + wylieBuffer.append(withA(glyphList)); + else + wylieBuffer.append(withoutA(glyphList)); + + glyphList.clear(); + needsVowel = true; + isLastVowel = false; + } + + wylieBuffer.append(ch); + } + else { + wylie = TibetanMachineWeb.getWylieForGlyph(dcs[i]); + + boolean containsBindu = false; + if (wylie.length() > 1 && wylie.charAt(wylie.length()-1) == TibetanMachineWeb.BINDU) { + char[] cArray = wylie.toCharArray(); + wylie = new String(cArray, 0, wylie.length()-1); + containsBindu = true; + } + + process_block: { + if (TibetanMachineWeb.isWyliePunc(wylie)) { + isLastVowel = false; + + if (glyphList.isEmpty()) + wylieBuffer.append(wylie); + + else { + if (needsVowel) + wylieBuffer.append(withA(glyphList)); + else + wylieBuffer.append(withoutA(glyphList)); + + wylieBuffer.append(wylie); //append the punctuation + + glyphList.clear(); + } + needsVowel = true; //next consonants are syllable onset, so we are awaiting vowel + } + + //isChar must come before isVowel because ACHEN has priority over WYLIE_aVOWEL + else if (TibetanMachineWeb.isWylieChar(wylie)) { + isLastVowel = false; + glyphList.add(dcs[i]); + } + + else if (TibetanMachineWeb.isWylieVowel(wylie)) { + if (isLastVowel) { + int len = wylieBuffer.length(); + int A_len = TibetanMachineWeb.A_VOWEL.length(); + + if (wylieBuffer.substring(len-A_len).equals(TibetanMachineWeb.A_VOWEL)) { + try { + if (wylie.equals(TibetanMachineWeb.i_VOWEL)) { + wylieBuffer.delete(len-A_len, len); + wylieBuffer.append(TibetanMachineWeb.I_VOWEL); + isLastVowel = false; + break process_block; + } + else if (wylie.equals(TibetanMachineWeb.reverse_i_VOWEL)) { + wylieBuffer.delete(len-A_len, len); + wylieBuffer.append(TibetanMachineWeb.reverse_I_VOWEL); + isLastVowel = false; + break process_block; + } + } + catch (StringIndexOutOfBoundsException se) { + } + + wylieBuffer.append(wylie); //append current vowel + isLastVowel = false; + } + else + wylieBuffer.append(wylie); //append current vowel + } + else { + int glyphCount = glyphList.size(); + boolean insertDisAmbig = false; + + if (0 != glyphCount) { + DuffCode top_dc = (DuffCode)glyphList.get(glyphCount-1); + String top_wylie = TibetanMachineWeb.getWylieForGlyph(top_dc); + + if (top_wylie.equals(TibetanMachineWeb.ACHEN)) { + glyphList.remove(glyphCount-1); + + if (glyphCount-1 == 0) + top_dc = null; + else { + insertDisAmbig = true; + top_dc = (DuffCode)glyphList.get(glyphCount-2); + } + } + + if (top_dc == null || !TibetanMachineWeb.getWylieForGlyph(top_dc).equals(TibetanMachineWeb.ACHUNG)) + wylieBuffer.append(withoutA(glyphList)); //append consonants in glyphList + else { + glyphCount = glyphList.size(); + glyphList.remove(glyphCount-1); + + if (glyphCount-1 != 0) + wylieBuffer.append(withA(glyphList)); + + wylieBuffer.append(TibetanMachineWeb.ACHUNG); + } + } + + if (insertDisAmbig) + wylieBuffer.append(TibetanMachineWeb.WYLIE_DISAMBIGUATING_KEY); + + wylieBuffer.append(wylie); //append vowel + + glyphList.clear(); + isLastVowel = true; + needsVowel = false; + } + } + else { //must be a stack + isLastVowel = false; + glyphList.add(dcs[i]); + } + } + + if (containsBindu) { + isLastVowel = false; + wylieBuffer.append(withoutA(glyphList)); + wylieBuffer.append(TibetanMachineWeb.BINDU); //append the bindu + glyphList.clear(); + } + } + } + + //replace TMW with Wylie + + if (!glyphList.isEmpty()) { + if (needsVowel) + wylieBuffer.append(withA(glyphList)); + else + wylieBuffer.append(withoutA(glyphList)); + } + + if (wylieBuffer.length() > 0) + return wylieBuffer.toString(); + else + return null; + } +} diff --git a/source/org/thdl/tib/text/package.html b/source/org/thdl/tib/text/package.html index 7df6b80..a14ce46 100644 --- a/source/org/thdl/tib/text/package.html +++ b/source/org/thdl/tib/text/package.html @@ -5,7 +5,7 @@ @(#)package.html - Copyright 2001 Tibetan and Himalayan Digital Library + Copyright 2001-2002 Tibetan and Himalayan Digital Library This software is the confidential and proprietary information of the Tibetan and Himalayan Digital Library. You shall use such @@ -33,6 +33,6 @@ Here, you can also find methods for installing and managing Tibetan keyboards. Four keyboards have been provided in this release, but users may also create their own keyboards.

Related Documentation

-@see org.thdl.tib.input +@see org.thdl.tib.input diff --git a/source/org/thdl/util/TeeStream.java b/source/org/thdl/util/TeeStream.java index c711ba9..ff457fe 100644 --- a/source/org/thdl/util/TeeStream.java +++ b/source/org/thdl/util/TeeStream.java @@ -22,7 +22,9 @@ import java.io.PrintStream; /** * All writes to this print stream are copied to two print streams of - * your choice. */ + * your choice. + * @author David Chandler + */ public class TeeStream extends PrintStream { private PrintStream out; public TeeStream(PrintStream out1, PrintStream out2) { diff --git a/source/org/thdl/util/ThdlAbstractAction.java b/source/org/thdl/util/ThdlAbstractAction.java new file mode 100644 index 0000000..82b421e --- /dev/null +++ b/source/org/thdl/util/ThdlAbstractAction.java @@ -0,0 +1,85 @@ +/* +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.util; + +import javax.swing.AbstractAction; +import javax.swing.Icon; +import java.awt.event.ActionEvent; + +import org.thdl.util.ThdlDebug; + +/** + * This ActionListener is like any other except in the way that it + * handles exceptions or errors thrown during the execution of + * actionPerformed(). Because event listeners are on + * threads, an exception during actionPerformed() is just + * printed out on the console by + * java.awt.EventDispatchThread.run(). It does not cause + * the program to terminate. In our code, it helps developers more + * quickly get to the root of a problem if the program terminates as + * soon after a problem as possible. + * + * Thus, this class calls System.exit(1) when an + * exception is throw by theRealActionPerformed(), which + * is the method that subclasses should implement. + * + * @see ThdlActionListener + * + * @author David Chandler + * + * There is a pertinent Usenet thread at http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=utf-8&threadm=6ntgl6%244hl%241%40tarantula.europe.shiva.com&rnum=2&prev=/groups%3Fq%3Dexception%2BactionPerformed%26hl%3Den%26lr%3D%26ie%3DUTF-8%26oe%3Dutf-8%26selm%3D6ntgl6%25244hl%25241%2540tarantula.europe.shiva.com%26rnum%3D2. + */ +public class ThdlAbstractAction extends AbstractAction { + + /** Just calls the super's constructor with the same args. */ + public ThdlAbstractAction(String s, Icon i) { + super(s, i); + } + + /** Subclasses don't override this. Instead, they override + * theRealActionPerformed(). + * @see #actionPerformed(ActionEvent) + */ + public final void actionPerformed(ActionEvent e) { + try { + theRealActionPerformed(e); + } catch (NoClassDefFoundError err) { + /* This is an especially common exception, and is in fact + the motivating force behind the ThdlActionListener + class and this class. Handle it well so that users + know what's up: */ + ThdlDebug.handleClasspathError(null, err); + } catch (Throwable t) { + System.err.println("THDL_ERR 107: This application failed due to the following exception: "); + t.printStackTrace(System.err); + System.exit(1); + } + } + + /** Subclasses should override this method to do the real action + * performed. + * @see #actionPerformed(ActionEvent) + */ + protected void theRealActionPerformed(ActionEvent e) + throws Throwable + { + /* do nothing */ + } +} + diff --git a/source/org/thdl/util/ThdlActionListener.java b/source/org/thdl/util/ThdlActionListener.java new file mode 100644 index 0000000..1e37521 --- /dev/null +++ b/source/org/thdl/util/ThdlActionListener.java @@ -0,0 +1,157 @@ +/* +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.util; + +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; + +import org.thdl.util.ThdlDebug; + +/** + * This ActionListener is like any other except in the way that it + * handles exceptions or errors thrown during the execution of + * actionPerformed(). Because event listeners are on + * threads, an exception during actionPerformed() is just + * printed out on the console by + * java.awt.EventDispatchThread.run(). It does not cause + * the program to terminate. In our code, it helps developers more + * quickly get to the root of a problem if the program terminates as + * soon after a problem as possible. + * + * Thus, this class calls System.exit(1) when an + * exception is throw by theRealActionPerformed(), which + * is the method that subclasses should implement. + * + * @see ThdlAbstractAction + * + * @author David Chandler + * + * Here is a pertinent Usenet thread from http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=utf-8&threadm=6ntgl6%244hl%241%40tarantula.europe.shiva.com&rnum=2&prev=/groups%3Fq%3Dexception%2BactionPerformed%26hl%3Den%26lr%3D%26ie%3DUTF-8%26oe%3Dutf-8%26selm%3D6ntgl6%25244hl%25241%2540tarantula.europe.shiva.com%26rnum%3D2: + + +
+From: Denis (denisb@europe.shiva.com)
+Subject: Exception in event handlers
+ 
+View this article only
+Newsgroups: comp.lang.java.gui, comp.lang.java.help, comp.lang.java.programmer
+Date: 1998/07/07
+
+Hi everyone !
+
+I've got a wee question regarding propagation of exceptions in event
+handlers :
+here's the fun :
+
+Say, you've got a method A() that is called from within an event handler
+(mostly it'll be in actionPerformed(ActionEvent) from ActionListener
+interface).
+Now this A() method produces an exception. This exception will be propagated
+up to the caller of actionPerformed which is into the event handler queue.
+This exception never gets propagated higher than the handler (and produces a
+message "exception occured in event handler")
+
+Now I would like this exception NOT to be caught there but to propagate
+until the program terminates or it is caught somewhere else.
+
+Is there a way to do that ? There's no mean to re-throw that exception as
+the event handler is part of the core java.
+
+Basically what I would like to do is catch ANY exception that occurs in a
+java application in order to display a message box, without having to put
+try{} catch{} blocks all other the place...
+
+Thanks for any help on that
+
+Chunk of code to illustrate :
+
+class MyClass extends Object implements ActionListener {
+// blah blah
+
+    public void actionPerformed(ActionEvent event) {
+        A(); //May throw an exception here but the actionPerformed is called
+        // from the event handler and the exception is caught into it
+
+        //So the actionPerformed finishes because of the exception but the
+        // rest of the app is likely to be in an unstable state, but still running
+    }
+
+   // So here the exception has been caught without an explicit try{}
+   // catch{} WHICH I DONT WANT
+
+   //You then can happily do something else....even if A() failed....
+
+}//MyClass
+
+Message 2 in thread
+From: David Holmes (dholmes@mri.mq.edu.au)
+Subject: Re: Exception in event handlers
+ 
+View this article only
+Newsgroups: comp.lang.java.gui, comp.lang.java.help, comp.lang.java.programmer
+Date: 1998/07/09
+
+Denis <denisb@europe.shiva.com> wrote in article
+<6ntgl6$4hl$1@tarantula.europe.shiva.com>...
+> Now I would like this exception NOT to be caught there but to propagate
+> until the program terminates or it is caught somewhere else.
+
+Once an exception is caught it is up to the catcher what to do with it. The
+event dispatch thread simply catches the exception and tells you about it.
+There is no way to make this exception go any further - indeed there is
+nowhere further for it to go. Exceptions occur per-thread and if the thread
+doesn't catch it the thread will terminate. A more sophisticated event
+mechanism might allow you to register a handler/listener to take some
+action when this occurred but the current AWT does not.
+
+David
+
+ + */ +public class ThdlActionListener implements ActionListener { + /** Subclasses don't override this. Instead, they override + * theRealActionPerformed(). + * @see #actionPerformed(ActionEvent) + */ + public final void actionPerformed(ActionEvent e) { + try { + theRealActionPerformed(e); + } catch (NoClassDefFoundError err) { + /* This is an especially common exception, and is in fact + the motivating force behind the ThdlActionListener + class. Handle it well so that users know what's up: */ + ThdlDebug.handleClasspathError(null, err); + } catch (Throwable t) { + System.err.println("THDL_ERR 106: This application failed due to the following exception: "); + t.printStackTrace(System.err); + System.exit(1); + } + } + + /** Subclasses should override this method to do the real action + * performed by this action listener. + * @see #actionPerformed(ActionEvent) + */ + protected void theRealActionPerformed(ActionEvent e) + throws Throwable + { + /* do nothing */ + } +} + diff --git a/source/org/thdl/util/ThdlDebug.java b/source/org/thdl/util/ThdlDebug.java new file mode 100644 index 0000000..759bff6 --- /dev/null +++ b/source/org/thdl/util/ThdlDebug.java @@ -0,0 +1,136 @@ +/* +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.util; + +import java.io.PrintStream; +import java.io.FileOutputStream; + +import org.thdl.util.TeeStream; + +/** + * This uninstantiable class provides assertions and the like in a + * JVM-version-independent fashion. + * @author David Chandler + */ +public class ThdlDebug { + /* FIXME: make this configurable. */ + static final String contactMsg + = " Please visit http://thdltools.sf.net/ or contact thdltools-devel@lists.sourceforge.net and give us a bug report so that we can improve the quality of this software."; + + /** Do not instantiate this class. */ + private ThdlDebug() { } + + /** Throws an unchecked exception if condition is not true. Note that + * unlike a real assertion, this happens always. We can certainly + * use AspectJ (with which I, DC, am intimately familiar) to avoid + * the overhead of such things in release builds if performance + * becomes a real issue. */ + public static void verify(boolean condition) { + verify(null, condition); + } + + /** Throws an unchecked exception if condition is not true. The + * exception's message will include the string msg if msg is not + * null. Note that unlike a real assertion, this happens always. + * We can certainly use AspectJ (with which I, DC, am intimately + * familiar) to avoid the overhead of such things in release + * builds if performance becomes a real issue. + * + * Throws a THDL-specific exception so that you can catch these + * specially in case you want to ignore them. + * + * @throws ThdlLazyException if condition is not true */ + public static void verify(String msg, boolean condition) + throws ThdlLazyException + { + if (!condition) { + throw new ThdlLazyException(new Error(((msg == null) + ? "THDL Tools sanity check: " + : msg) + + "An assertion failed. This means that there is a bug in this software." + + contactMsg)); + } + } + + /** Call this from control-flow paths that are not well thought + * out. For example, if you have to catch an IOException, but + * you're fairly certain that it'll never be thrown, call this + * function if it is indeed thrown. Developers can set the + * THDL_DIE_EAGERLY property to true (using 'java + * -DTHDL_DIE_ON_IFFY_CODE=true') in order to test the + * code's robustness. + * + * Throws a THDL-specific exception so that you can catch these + * specially in case you want to ignore them. + * + * @throws ThdlLazyException if the THDL_DIE_ON_IFFY_CODE system + * property is set to "true" */ + public static void noteIffyCode() + throws ThdlLazyException + { + + /* FIXME: find all calls to this function and rethink or shore + up the calling code. */ + + if (Boolean.getBoolean("THDL_DIE_ON_IFFY_CODE")) + throw new ThdlLazyException(new Error("You've reached some iffy code, some code that's not well thought-out. Because you invoked the Java runtime environment with the property THDL_DIE_ON_IFFY_CODE set to true (developers: use 'ant -Dthdl.die.on.iffy=false' to prevent this), the program is now aborting.")); + } + + /** Exits the program with a message that the CLASSPATH is not set + properly. */ + public static void handleClasspathError(String whoseWhat, Throwable error) { + System.err.println(((whoseWhat == null) ? "Your CLASSPATH" : whoseWhat) + + " is not set properly."); + + /* FIXME */ + System.err.println("Note that Savant and QuillDriver CANNOT be invoked via the"); + System.err.println("'java -jar Savant-xyz.jar' option, because that silently ignores"); + System.err.println("the CLASSPATH. This means that double-clicking them won't work"); + System.err.println("either, because we don't set the JARs' manifest files to contain"); + System.err.println("Class-path attributes. See installation instructions."); /* FIXME: we don't HAVE installation instructions, do we? */ + System.err.println(""); + System.err.println("Details: Missing class: " + + ((error == null) + ? "unknown!" : error.getMessage())); + if (Boolean.getBoolean("THDL_DEBUG")) { + System.err.println("Details: Stack trace: " + + ((error == null) + ? "unknown!" : error.getMessage())); + error.printStackTrace(System.err); + } + System.exit(1); + } + + /** Sets it up so that a call to System.out or System.err prints + * to standard output/error but ALSO prints to the log file named + * logFile. */ + public static void attemptToSetUpLogFile(String logFile) { + try { + PrintStream logFilePrintStream + = new PrintStream(new FileOutputStream(logFile)); + PrintStream psOut = new TeeStream(System.out, logFilePrintStream); + PrintStream psErr = new TeeStream(System.err, logFilePrintStream); + System.setErr(psErr); + System.setOut(psOut); + } catch (Exception e) { + /* don't let this stop us. */ + noteIffyCode(); + } + } +}; diff --git a/source/org/thdl/util/package.html b/source/org/thdl/util/package.html new file mode 100644 index 0000000..8202974 --- /dev/null +++ b/source/org/thdl/util/package.html @@ -0,0 +1,22 @@ + + + + + + +Provides classes and methods not specific to any one application. +

+These classes aim to be of general use to the THDL's Java programs. + + diff --git a/source/overview.html b/source/overview.html new file mode 100644 index 0000000..203ba97 --- /dev/null +++ b/source/overview.html @@ -0,0 +1,112 @@ + + + + + + +Provides facilities for manipulating text, audio, and video in Tibetan and other Himalayan languages. + +

+From the THDL Tools project's + + News archive: + +

+Posted By: eg3p
+Date: 2002-09-26 12:14
+Summary:THDL Projects and Goals
+
+ +

+Hello. You'll see that we at the Tibetan and Himalayan Digital Library +have been developing several Tibetan language and culture related +software tools, all of which take advantage of Tony Duff's free +cross-platform TibetanMachineWeb fonts. We think these tools have +reached a level of development that they now need the active +participation of the larger programming community in order to +progress. +

+ +

+On the one hand, we've developed Java-based tools for inputting and +displaying Tibetan script (Jskad), as well as for the transcription of +Tibetan-language videos (QuillDriver), and the synchronized playback +of videos along with their associated transcripts (Savant). +

+ +

+On the other hand, recognizing the ubiquity of Word, we've also +developed Visual Basic macros for inputting Tibetan script (WylieWord) +and diacritics useful for Asian studies (DiacriticsForWord, +DiacriticsForWin2k) into Microsoft Word. +

+ +

+The Java side of our project has several goals. First, we'd like to +create a high quality cross-platform word processor/display engine +optimized for Tibetan script input and display. This probably means +embedding Jskad in an established open source text editor. +

+ +

+Second, we are developing educational and research-oriented software +that takes advantage of these basic Tibetan display +capabilities. QuillDriver, designed for transcription of videos, and +Savant, an integrated tool for learning language from digital video, +are examples of this. Here, several tasks are on the horizon: merging +QuillDriver and Savant into the same tool, which has both edit +(QuillDriver) and display (Savant) modes; and integrating both with +Tibetan dictionary, grammar and pedagogy modules also being developed +at the THDL. Finally, note that unlike Jskad, both QuillDriver and +Savant are meant to be truly multilingual, and not just specialized +Tibetan software. +

+ +

+Still, we know that no matter what the open source community does, +Microsoft Word will still remain the most dominant (and arguably the +best) word-processor. There will always be people, and always +occasions, to type Tibetan into Word. Therefore, we are also pushing +our Visual Basic macros for Tibetan and diacritic input. Our main +goals with these tools is to make them as bug-free and user-friendly +as possible, and to develop stable Mac versions. Another possible goal +would be to extend these tools to work with other Office applications, +such as Excel or Access. +

+ +

+Given the various projects, we are setting up several forums for +specialized discussion: +

+ +

+1) General +2) Jskad +3) QuillDriver/Savant +4) WylieWord +5) Diacritics +

+ +

+Please occasionally consult those forums of interest to you. Also, +please take a look at the mailing lists and subscribe to those you +want to follow. +

+ +

+Spread the word: THDL Tools are on SourceForge! +

+ + +