584 lines
15 KiB
Java
584 lines
15 KiB
Java
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 ;
|
|
import java.text.StringCharacterIterator ;
|
|
|
|
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 () ) ;
|
|
}
|
|
|
|
/**
|
|
* close the window
|
|
*/
|
|
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
|
|
*
|
|
* @return true if the dict frame does not display any data
|
|
*/
|
|
public boolean isEmpty ()
|
|
{
|
|
return ( original.getText ().length () > 0 ? false : true ) ;
|
|
}
|
|
|
|
/**
|
|
* set the original
|
|
*
|
|
* @param text - has the Wylie roman text
|
|
* @param realDoc - ref to the actual document being edited
|
|
* @param startPos
|
|
* @param 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
|
|
*
|
|
* @param text - description as retrieved form TibetanScanner derived objs
|
|
*/
|
|
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
|
|
//
|
|
|
|
text = handleSanskrit ( text ) ;
|
|
|
|
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 () ;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gotoList
|
|
* force the focus to the entry list box
|
|
*
|
|
*/
|
|
public void gotoList ()
|
|
{
|
|
entryList.requestFocusInWindow () ;
|
|
entryList.setSelectedIndex ( 0 ) ;
|
|
}
|
|
|
|
/**
|
|
* handleSanskrit
|
|
* converts the dictionary entry text so that romanized Sanskrit leters are
|
|
* displayed correctly (long a vs A etc)
|
|
*
|
|
* @param inText - text as retrieved from TibetanScanner
|
|
* @return converted text
|
|
*/
|
|
protected String handleSanskrit ( String inText )
|
|
{
|
|
String outText = "" ;
|
|
StringCharacterIterator it = new StringCharacterIterator ( inText ) ;
|
|
|
|
//
|
|
// we have three states
|
|
//
|
|
final int DEFAULT = 0 ;
|
|
final int INBRACES = 1 ;
|
|
final int INWORD = 2 ;
|
|
|
|
//
|
|
// state is the current state
|
|
// word is the recently collected piece of text
|
|
// wasBraces is true if we are just after a {XX} sequence was encountered
|
|
//
|
|
int state = DEFAULT ;
|
|
String word = "" ;
|
|
boolean wasBraces = false ;
|
|
|
|
//
|
|
// loop through the entire text, find whatever is likely to be a Sanskrit word
|
|
//
|
|
// the decision whether a word could be Sanskrit is based on two criteria:
|
|
// 1. if it appears immediately after a {XX} sequence it's assumed to be Sanskrit
|
|
// 2. otherwise if it contains a sequence of <lowercase><uppercase>
|
|
// in which case uppercase is likely to be a letter that needs conversion
|
|
//
|
|
for ( char c = it.first (); c != StringCharacterIterator.DONE; c = it.next () )
|
|
{
|
|
switch ( state )
|
|
{
|
|
case DEFAULT:
|
|
if ( Character.isLetter (c) )
|
|
{
|
|
word = String.valueOf (c) ;
|
|
state = INWORD ;
|
|
}
|
|
else if ( '{' == c )
|
|
{
|
|
wasBraces = false ;
|
|
state = INBRACES ;
|
|
}
|
|
else
|
|
{
|
|
wasBraces = false ;
|
|
outText += c ;
|
|
}
|
|
break ;
|
|
|
|
case INBRACES:
|
|
if ( '}' != c )
|
|
{
|
|
word += c ;
|
|
}
|
|
else
|
|
{
|
|
outText += "{" + word + "}" ;
|
|
word = "" ;
|
|
wasBraces = true ;
|
|
state = DEFAULT ;
|
|
}
|
|
break ;
|
|
|
|
case INWORD:
|
|
if ( Character.isLetter (c) )
|
|
{
|
|
word += c ;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// looks like a Sanskrit word, let's convert it
|
|
//
|
|
outText += processSanskrit ( word, wasBraces ) + c ;
|
|
word = "" ;
|
|
state = DEFAULT ;
|
|
}
|
|
break ;
|
|
}
|
|
}
|
|
|
|
|
|
return outText ;
|
|
}
|
|
|
|
/**
|
|
* isLikelyToBeSanskrit
|
|
*
|
|
* the criterion is :
|
|
* the word contains a sequence of <lowercase><uppercase>
|
|
*
|
|
* @param word
|
|
* @return true is word is likely to be Sanskrit
|
|
*
|
|
*/
|
|
protected boolean isLikelyToBeSanskrit ( String word )
|
|
{
|
|
if ( word.matches ( ".*[a-z][A-Z].*" ) )
|
|
return true ;
|
|
|
|
return false ;
|
|
}
|
|
|
|
/**
|
|
* processSanskrit
|
|
*
|
|
* @param word
|
|
* @param forceSanskrit
|
|
* @return converted word
|
|
*/
|
|
protected String processSanskrit ( String word, boolean forceSanskrit )
|
|
{
|
|
//
|
|
//
|
|
//
|
|
if ( forceSanskrit || isLikelyToBeSanskrit ( word ) )
|
|
{
|
|
word = word.replaceAll ( "A", "a\u0305" ) ;
|
|
word = word.replaceAll ( "I", "i\u0305" ) ;
|
|
word = word.replaceAll ( "U", "u\u0305" ) ;
|
|
word = word.replaceAll ( "S", "s\u0323" ) ;
|
|
word = word.replaceAll ( "D", "d\u0323" ) ;
|
|
word = word.replaceAll ( "T", "t\u0323" ) ;
|
|
word = word.replaceAll ( "M", "m\u0307" ) ;
|
|
word = word.replaceAll ( "N", "n\u0307" ) ;
|
|
word = word.replaceAll ( "H", "h\u0323" ) ;
|
|
word = word.replaceAll ( "R", "r\u0323" ) ;
|
|
word = word.replaceAll ( "L", "l\u0323" ) ;
|
|
word = word.replaceAll ( "z", "s\u0301" ) ;
|
|
}
|
|
|
|
return word ;
|
|
}
|
|
|
|
/**
|
|
* unit test
|
|
*/
|
|
public static void main ( String [] args )
|
|
{
|
|
new DictionaryFrame () ;
|
|
}
|
|
}
|