diff --git a/source/org/thdl/tib/input/DictionaryFrame.java b/source/org/thdl/tib/input/DictionaryFrame.java new file mode 100644 index 0000000..1cad98f --- /dev/null +++ b/source/org/thdl/tib/input/DictionaryFrame.java @@ -0,0 +1,425 @@ +package org.thdl.tib.input ; + +import javax.swing.* ; +import javax.swing.text.* ; +import javax.swing.text.StyleConstants.FontConstants ; +import java.awt.GridBagLayout ; +import java.awt.GridBagConstraints ; +import java.awt.Insets ; +import java.awt.Rectangle ; +import java.awt.Font ; +import java.awt.Color ; +import java.awt.Component ; +import java.awt.event.KeyEvent ; +import java.awt.event.KeyAdapter ; +import java.awt.event.ActionListener ; +import java.awt.event.ActionEvent ; +import javax.swing.event.ListSelectionListener ; +import javax.swing.event.ListSelectionEvent ; +import java.util.concurrent.locks.ReentrantLock ; + +class DictionaryFrame extends JFrame implements ListSelectionListener +{ + // + // the fields to display .. + // + protected JTextPane original ; // .. the original text - romanized and tibetan + protected JTextArea pronounciation ; // .. the pronounciation + protected DefaultListModel listModel ; // + protected JList entryList ; // .. list of dictionary keywords + protected JTextPane description ; // .. sequence of dictionary entries + protected JScrollPane descriptionPane ; // + protected JButton closeButton ; + + protected Font fontSerif ; + protected Font fontSansSerif ; + + protected ReentrantLock listLock ; + + // + // entryList contains MyListElement-s instead of just String-s + // (we want a number to be associated with each element to store the position within description) + // + class MyListElement + { + MyListElement ( String theStr, int theData ) + { + str = theStr ; + data = theData ; + } + + public void setIntData ( int theData ) + { + data = theData ; + } + + public int getIntData () + { + return data ; + } + + public String toString () + { + return str ; + } + + protected String str ; + protected int data ; + }; + + DictionaryFrame () + { + super ( "Dictionary" ) ; + init ( null ) ; + } + + DictionaryFrame ( Component parent ) + { + super ( "Dictionary" ) ; + init ( parent ) ; + } + + void init ( Component parent ) + { + listLock = new ReentrantLock () ; + + fontSerif = new Font ( "serif", Font.PLAIN, 12 ) ; + fontSansSerif = new Font ( "sansserif", Font.PLAIN, 12 ) ; + + // + // layout + // + GridBagLayout gridbag = new GridBagLayout () ; + GridBagConstraints c = new GridBagConstraints () ; + + getContentPane ().setLayout ( gridbag ) ; + + // + // children + // + original = new JTextPane () ; + pronounciation = new JTextArea ( "" ) ; + + listModel = new DefaultListModel () ; + + entryList = new JList ( listModel ) ; + entryList.addListSelectionListener ( this ) ; + + description = new JTextPane () ; + descriptionPane = new JScrollPane ( description, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_NEVER ) ; + + closeButton = new JButton ( "Close" ) ; + closeButton.setMnemonic ( 'c' ) ; + closeButton.addActionListener ( new ActionListener () { public void actionPerformed ( ActionEvent e ) { closeWindow () ; } } ) ; + + original.setEditable ( false ) ; + pronounciation.setEditable ( false ) ; + description.setEditable ( false ) ; + + // + // add them + // + c.insets = new Insets ( 1, 1, 1, 1 ) ; + c.anchor = GridBagConstraints.NORTHWEST ; + c.weightx = 1.0 ; + c.weighty = 0.0 ; + c.gridheight = 1 ; + c.gridwidth = GridBagConstraints.REMAINDER ; + c.fill = GridBagConstraints.HORIZONTAL ; + + gridbag.setConstraints ( original, c ) ; + getContentPane ().add ( original ) ; + + c.anchor = GridBagConstraints.NORTHWEST ; + c.weightx = 1.0 ; + c.weighty = 0.0 ; + c.gridheight = 1 ; + c.gridwidth = GridBagConstraints.REMAINDER ; + c.fill = GridBagConstraints.HORIZONTAL ; + + gridbag.setConstraints ( pronounciation, c ) ; + getContentPane ().add ( pronounciation ) ; + + c.weighty = 1.0 ; + c.fill = GridBagConstraints.BOTH ; + c.gridheight = 4 ; + c.weightx = 0.0 ; + c.gridwidth = 1 ; + + gridbag.setConstraints ( entryList, c ) ; + getContentPane ().add ( entryList ) ; + + c.fill = GridBagConstraints.BOTH ; + c.gridheight = 4 ; + c.gridwidth = GridBagConstraints.REMAINDER ; + c.weightx = 3.0 ; + + gridbag.setConstraints ( descriptionPane, c ) ; + getContentPane ().add ( descriptionPane ) ; + + c.anchor = GridBagConstraints.CENTER ; + c.weightx = 1.0 ; + c.weighty = 0.0 ; + c.gridx = GridBagConstraints.RELATIVE ; + c.gridy = GridBagConstraints.RELATIVE ; + c.gridheight = 1 ; + c.gridwidth = GridBagConstraints.RELATIVE ; + c.fill = GridBagConstraints.HORIZONTAL ; + + gridbag.setConstraints ( closeButton, c ) ; + getContentPane ().add ( closeButton ) ; + + // + // set fonts + // + original.setFont ( fontSansSerif ) ; + pronounciation.setFont ( fontSansSerif ) ; + entryList.setFont ( fontSansSerif ) ; + description.setFont ( fontSansSerif ) ; + + // + // we need F12 to toggle the visible state of the dictionary window + // + KeyAdapter keyAdapter = new KeyAdapter () + { + public void keyPressed ( KeyEvent ev ) + { + if ( KeyEvent.VK_F12 == ev.getKeyCode () || + KeyEvent.VK_ESCAPE == ev.getKeyCode () ) + toggleVisible () ; + } + }; + original.addKeyListener ( keyAdapter ) ; + pronounciation.addKeyListener ( keyAdapter ) ; + entryList.addKeyListener ( keyAdapter ) ; + description.addKeyListener ( keyAdapter ) ; + + // + // finish + // + pack () ; + + // + // place the window on top of the parent frame, moving it slightly + // + if ( parent != null ) + setLocation ( parent.getLocation ().x, parent.getLocation ().y ) ; + + setSize ( 500, 500 ) ; + } + + /** + * toggle the visibility of this window + */ + void toggleVisible () + { + setVisible ( !isVisible () ) ; + } + + /** + * + */ + void closeWindow () + { + setVisible ( false ) ; + } + + /** + * clear all fields + */ + public void reset () + { + // + // clear all fields + // + listLock.lock () ; + try + { + original.setText ( "" ) ; + pronounciation.setText ( "" ) ; + + description.setText ( "" ) ; + listModel.clear () ; + } + finally + { + listLock.unlock () ; + } + } + + /** + * isEmpty + */ + public boolean isEmpty () + { + return ( original.getText ().length () > 0 ? false : true ) ; + } + + /** + * set the original + * + * text - has the Wylie roman text + * realDoc - ref to the actual document being edited + * startPos, endPos - location of the text in the actual document being edited + */ + public void setOriginal ( String text, DefaultStyledDocument realDoc, int startPos, int endPos ) + { + AbstractDocument doc = (AbstractDocument)original.getDocument () ; + + SimpleAttributeSet as = new SimpleAttributeSet () ; + as.addAttribute ( FontConstants.Family, "serif" ) ; + try + { + doc.insertString ( doc.getLength (), text + " ", as ) ; + } + catch ( BadLocationException e ) + { + // this should never happen + } + + SimpleAttributeSet as1 = new SimpleAttributeSet () ; + + for ( int pos = startPos; pos <= endPos; pos++ ) + { + Element el = realDoc.getCharacterElement ( pos ) ; + + try + { + doc.insertString ( doc.getLength (), realDoc.getText ( pos, 1 ), el.getAttributes () ) ; + } + catch ( BadLocationException e ) + { + // this should never happen + } + } + } + + /** + * set the pronounciation field + */ + public void setPronounciation ( String text ) + { + // + // set the pronounciation field + // + pronounciation.setText ( text ) ; + } + + /** + * add a dictionary entry + */ + public void addDescription ( String text ) + { + // + // add the keyword to entryList. we assume the keyword is anything in the + // dictionary entry that precedes the first dash + // + + listLock.lock () ; + + try + { + Document doc = description.getDocument () ; + + try + { + int len = doc.getLength() ; + if ( len > 0 ) + { + doc.insertString ( len, "\n\n", null ) ; + len = doc.getLength () ; + } + + int dashIndex = text.indexOf ( "-" ) ; + String keyword = text.substring ( 0, dashIndex - 1 ) ; + String entry = text.substring ( dashIndex + 1 ) ; + + // + // add keyword to the list box + // + listModel.addElement ( new MyListElement ( keyword, len ) ) ; + + SimpleAttributeSet attrSet = new SimpleAttributeSet () ; + StyleConstants.setBold ( attrSet, true ) ; + StyleConstants.setUnderline ( attrSet, true ) ; + + // + // add entry to the list box + // + doc.insertString ( len, keyword, attrSet ) ; + len = doc.getLength () ; + + StyleConstants.setBold ( attrSet, true ) ; + StyleConstants.setUnderline ( attrSet, false ) ; + doc.insertString ( len, entry, null ) ; + } + catch ( Exception e ) + { + } + } + finally + { + listLock.unlock () ; + } + } + + /** + * called when entryList selection changes + */ + public void valueChanged ( ListSelectionEvent e ) + { + // + // get the position of the dictionary entry identified + // by the selected entryList item. + // + + listLock.lock () ; + + try + { + MyListElement elem = ((MyListElement)entryList.getSelectedValue ()) ; + if ( null != elem ) + { + int pos = elem.getIntData () ; + + try + { + // + // make sure the part of description associated with the item + // is visible; TODO this doesn't work quite well + // + Rectangle rect = description.modelToView ( pos ) ; + rect.setSize ( (int)rect.getWidth (), descriptionPane.getHeight () - 10 ) ; + description.scrollRectToVisible ( rect ) ; + } + catch ( BadLocationException ble ) + { + // + // this should never happen + // + System.err.println ( "DictionaryFrame.valueChanged .. Incorrect argument passed to modelToView." ) ; + } + } + } + finally + { + listLock.unlock () ; + } + } + + public void gotoList () + { + entryList.requestFocusInWindow () ; + entryList.setSelectedIndex ( 0 ) ; + } + + /** + * unit test + */ + public static void main ( String [] args ) + { + new DictionaryFrame () ; + } +} diff --git a/source/org/thdl/tib/input/DictionaryLoadState.java b/source/org/thdl/tib/input/DictionaryLoadState.java new file mode 100644 index 0000000..7e9ac8e --- /dev/null +++ b/source/org/thdl/tib/input/DictionaryLoadState.java @@ -0,0 +1,12 @@ +package org.thdl.tib.input ; + +/** + * + */ +class DictionaryLoadState +{ + public static final int UNDEFINED = 0 ; + public static final int READY = 1 ; + public static final int ERROR = -1 ; +} + diff --git a/source/org/thdl/tib/input/GlobalResourceHolder.java b/source/org/thdl/tib/input/GlobalResourceHolder.java new file mode 100644 index 0000000..83fa823 --- /dev/null +++ b/source/org/thdl/tib/input/GlobalResourceHolder.java @@ -0,0 +1,411 @@ +/** + * + * + */ + +package org.thdl.tib.input ; + +import org.thdl.tib.scanner.TibetanScanner ; +import org.thdl.tib.scanner.LocalTibetanScanner ; +import org.thdl.tib.scanner.RemoteTibetanScanner ; +import org.thdl.tib.scanner.Word ; + +import org.thdl.tib.input.SettingsChangeListener ; +import org.thdl.tib.input.SettingsServiceProvider ; +import org.thdl.tib.input.DictionaryLoadState ; + +import java.awt.GraphicsEnvironment ; +import java.awt.Font ; +import java.net.URL ; +import java.net.URLConnection ; +import java.io.InputStream ; +import java.util.HashMap ; +import java.util.Observer ; +import java.util.Observable ; + +public class GlobalResourceHolder +{ + protected static final int NUM_FONTS = 10 ; + protected static final String FONT_NAME_PREFIX = "TibetanMachineWeb" ; + + private static boolean flagIsUsingLocalFonts = false ; + private static Font baseTMWFont [] = null ; + private static Font derivedTMWFont [] = null ; + private static HashMap nameToNumber = null ; + private static TibetanScanner tibetanScanner = null ; + private static SettingsServiceProvider settingsServiceProvider ; + + private static boolean dictionaryValid = false ; + private static boolean dictionaryEnabled = false ; + private static boolean dictionaryLocal = false ; + private static String dictionaryPath = "" ; + + private static int dictionaryLoadState = DictionaryLoadState.UNDEFINED ; + private static Object dictionaryLoadLock = null ; + + //static class Listener implements SettingsChangeListener + static class Listener implements Observer + { + /** + * update + * + */ + public void update ( Observable o, Object arg ) + { + onSettingsChange ( ((Integer)arg).intValue () ) ; + } + + /** + * onSettingsChange + * + */ + public void onSettingsChange ( int setting ) + { + if ( null == GlobalResourceHolder.settingsServiceProvider ) + return ; + + if ( SettingsChangeListener.DICTIONARY_SETTINGS == setting ) + { + // + // compare with the current settings + // + boolean newDictionaryEnabled = GlobalResourceHolder. + settingsServiceProvider.getDictionarySettingsEnabled () ; + boolean newDictionaryLocal = GlobalResourceHolder. + settingsServiceProvider.getDictionarySettingsLocal () ; + String newDictionaryPath = GlobalResourceHolder. + settingsServiceProvider.getDictionarySettingsPath () ; + + + if ( newDictionaryEnabled != GlobalResourceHolder.dictionaryEnabled || + newDictionaryLocal != GlobalResourceHolder.dictionaryLocal || + ! newDictionaryPath.equals ( GlobalResourceHolder.dictionaryPath ) ) + { + GlobalResourceHolder.dictionaryEnabled = newDictionaryEnabled ; + GlobalResourceHolder.dictionaryLocal = newDictionaryLocal ; + GlobalResourceHolder.dictionaryPath = newDictionaryPath ; + + + GlobalResourceHolder.tibetanScanner = GlobalResourceHolder.loadDictionaryScanner () ; + } + } + } + } + + static Listener listenerObj = null ; + + static + { + listenerObj = new Listener () ; + + // + // we have no service provider until the client class is born + // + settingsServiceProvider = null ; + + // + // init the font array + // + // + baseTMWFont = new Font [ NUM_FONTS ] ; + derivedTMWFont = new Font [ NUM_FONTS ] ; + + // + // if TibetanMachineWeb fonts are not installed + // load the fonts from the .jar file and set + // the flag + // + if ( ! isTMWInstalled () ) + setLocalFonts () ; + + // + // initialize the dictionary stuff + // + tibetanScanner = null ; + + dictionaryValid = false ; + dictionaryEnabled = false ; + dictionaryLocal = false ; + dictionaryPath = "" ; + + dictionaryLoadLock = new Object () ; + } + + /** + */ + public static Listener getListener () + { + return listenerObj ; + } + + /** + * + */ + public static int getDictionaryLoadState () + { + int state ; + synchronized ( dictionaryLoadLock ) + { + state = dictionaryLoadState ; + } + + return state ; + } + + public static void waitForDictionary () + { + synchronized ( dictionaryLoadLock ) + { + + } + } + + + + /* + * loadDictionaryScanner + * + */ + protected static TibetanScanner loadDictionaryScanner () + { + //return new LocalTibetanScanner ( "F:/tmp/free/free" ) ; + TibetanScanner ts = null ; + + if ( ! dictionaryEnabled ) + return null ; + + dictionaryValid = false ; + + synchronized ( dictionaryLoadLock ) + { + dictionaryLoadState = DictionaryLoadState.UNDEFINED ; + + try + { + if ( dictionaryLocal ) + { + ts = new LocalTibetanScanner ( dictionaryPath ) ; + dictionaryValid = true ; + } + else + { + ts = new RemoteTibetanScanner ( dictionaryPath ) ; + dictionaryValid = true ; + } + + dictionaryLoadState = DictionaryLoadState.READY ; + } + catch ( Exception e ) + { + System.err.println ( "TibetanScanner.loadDictionaryScanner () --> " + e.toString () ) ; + dictionaryLoadState = DictionaryLoadState.ERROR ; + } + } + + return ts ; + } + + /* + * isTMWInstalled () + * + * returns true if TibetanMachineWeb fonts are + * installed on the system. + */ + protected static boolean isTMWInstalled () + { + String [] familyNames = + GraphicsEnvironment.getLocalGraphicsEnvironment ().getAvailableFontFamilyNames () ; + + int mask = 0 ; + + for ( int i = 0; i < familyNames.length; i++ ) + { + String family = familyNames [i] ; + + if ( family.startsWith ( FONT_NAME_PREFIX ) ) + { + int fontNum = 0 ; + + if ( family == FONT_NAME_PREFIX ) + { + fontNum = 1 ; + } + else + { + fontNum = Character.digit ( family.charAt ( family.length () - 1 ), 10 ) ; + + if ( fontNum < 0 || fontNum > NUM_FONTS ) + { + fontNum = 0 ; + } + } + + if ( fontNum > 0 ) + { + mask |= ( 1 << (fontNum-1) ) ; + if ( ( ( 1 << NUM_FONTS ) - 1 ) == mask ) + return true ; + } + } + } + + return false ; + } + + /* + * setLocalFonts () + * + * loads the fonts from Fonts directory in + * the .jar file, if the dir exists + * + */ + protected static void setLocalFonts () + { + nameToNumber = new HashMap () ; + + for ( int i = 0; i < NUM_FONTS; i++ ) + { + String suffix = ((i>0)?String.valueOf (i):"") ; + URL url = + GlobalResourceHolder.class.getResource ( "/Fonts/TibetanMachineWeb/timwn" + suffix + ".ttf" ) ; + + if ( null != url ) + { + try + { + URLConnection conn = url.openConnection () ; + InputStream is = conn.getInputStream () ; + baseTMWFont [i] = Font.createFont ( Font.TRUETYPE_FONT, is ) ; + is.close () ; + } + catch ( Exception e ) + { + System.err.println ( "Failed to load fonts from JAR !" ) ; + flagIsUsingLocalFonts = false ; + return ; + } + } + + // + // we use a hash map to translate font names into their numbers (indices) + // + nameToNumber.put ( FONT_NAME_PREFIX + ( ( 0 == i ) ? "" : String.valueOf (i) ), new Integer ( i ) ) ; + } + + flagIsUsingLocalFonts = true ; + } + + /* + * setFontSize + * + * notifies the GlobalResource about the most probably used font size + * + */ + protected static void setFontSize ( int fontSize ) + { + if ( !flagIsUsingLocalFonts ) + return ; + + if ( null == derivedTMWFont [0] || fontSize != derivedTMWFont [0].getSize () ) + { + for ( int i = 0; i < NUM_FONTS; i++ ) + { + derivedTMWFont [i] = baseTMWFont [i].deriveFont ( Font.PLAIN, fontSize ) ; + } + } + } + + /* + * getFont ( fontName, size ) + * + */ + public static Font getFont ( String fontName, int fontSize ) + { + Integer intObj = (Integer)nameToNumber.get ( fontName ) ; + + if ( intObj != null ) + { + int fontNum = intObj.intValue () ; + return getFont ( fontNum, fontSize ) ; + } + + return null ; + } + + /* + * getFont ( fontNum, size ) + * + */ + public static Font getFont ( int fontNum, int fontSize ) + { + Font font = null ; + + if ( flagIsUsingLocalFonts ) + { + if ( null == derivedTMWFont [fontNum] || fontSize != derivedTMWFont [fontNum].getSize () ) + { + font = derivedTMWFont [fontNum] = baseTMWFont [fontNum].deriveFont ( Font.PLAIN, fontSize ) ; + } + } + + return font ; + } + + /* + * isUsingLocalFonts () + * + * returns true if the app should use the fonts from the package .jar file + * (==> fonts are not installed on the system) + * + */ + public static boolean isUsingLocalFonts () + { + return flagIsUsingLocalFonts ; + } + + /* + * getTibetanScanner () + * + * returns the dictionary scanner object or null + */ + public static TibetanScanner getTibetanScanner () + { + return tibetanScanner ; + } + + /** + * + */ + public static void setSettingsServiceProvider ( SettingsServiceProvider theSettingsServiceProvider ) + { + settingsServiceProvider = theSettingsServiceProvider ; + theSettingsServiceProvider.getObservable ().addObserver ( listenerObj ) ; + } + + /** + * + */ + public static void removeSettingsServiceProvider + ( SettingsServiceProvider theSettingsServiceProvider ) + { + settingsServiceProvider = null ; + theSettingsServiceProvider.getObservable ().deleteObserver ( listenerObj ) ; + } + + /** + * + */ + public static boolean isDictionaryValid () + { + return dictionaryValid ; + } + + /** + * + */ + public static boolean isDictionaryEnabled () + { + return dictionaryEnabled ; + } + +}; diff --git a/source/org/thdl/tib/input/SettingsChangeListener.java b/source/org/thdl/tib/input/SettingsChangeListener.java new file mode 100644 index 0000000..bd23bc1 --- /dev/null +++ b/source/org/thdl/tib/input/SettingsChangeListener.java @@ -0,0 +1,41 @@ +/* +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.input; + +/** +* +* +* +* +* +* +* +* +* +* +* @author Michal Podhorecki */ + +public interface SettingsChangeListener +{ + public static final int DICTIONARY_SETTINGS = 1 ; + + void onSettingsChange ( int setting ) ; + +} // interface SettingsChangeListener + diff --git a/source/org/thdl/tib/input/SettingsServiceProvider.java b/source/org/thdl/tib/input/SettingsServiceProvider.java new file mode 100644 index 0000000..92539d1 --- /dev/null +++ b/source/org/thdl/tib/input/SettingsServiceProvider.java @@ -0,0 +1,34 @@ +/* +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.input; + +import java.util.Observable ; + + +/** +* +* @author Edward Garrett, Tibetan and Himalayan Digital Library */ +public interface SettingsServiceProvider +{ + boolean getDictionarySettingsEnabled () ; + boolean getDictionarySettingsLocal () ; + String getDictionarySettingsPath () ; + + Observable getObservable () ; +} // interface SettingsServiceProvider