/*
* Open Source Physics software is free software as described near the bottom of this code file.
*
* For additional information and documentation on Open Source Physics please see:
* <http://www.opensourcephysics.org/>
*/
package org.opensourcephysics.tools;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.WindowConstants;
import javax.swing.event.TableModelEvent;
import org.opensourcephysics.controls.ControlsRes;
import org.opensourcephysics.controls.OSPLog;
import org.opensourcephysics.controls.XML;
import org.opensourcephysics.controls.XMLControl;
import org.opensourcephysics.controls.XMLControlElement;
import org.opensourcephysics.controls.XMLTable;
import org.opensourcephysics.controls.XMLTableModel;
import org.opensourcephysics.display.Hidable;
import org.opensourcephysics.display.OSPRuntime;
import org.opensourcephysics.display.TextFrame;
/**
* This provides a GUI for creating and editing string resources associated
* with a class. Resources are stored in properties files
* with the same name and located in the same folder as the class.
*
* @author Douglas Brown
* @version 1.0
*/
public class TranslatorTool extends JFrame implements Tool, Hidable, Translator {
// instance fields
private Dimension dim = new Dimension(320, 240);
private Map<Class<?>, Map<String, String>> defaultProps // maps class to default properties map (name->value translations)
= new HashMap<Class<?>, Map<String, String>>();
private Map<Class<?>, Map<String, Map<String, String>>> classes // maps class to map of language->properties
= new HashMap<Class<?>, Map<String, Map<String, String>>>();
private Map<Object, Class<?>> associates = new HashMap<Object, Class<?>>(); // maps object to class
private Set<Map<String, String>> changed = new HashSet<Map<String, String>>(); // contains properties with unsaved changes
private Locale locale = Locale.getDefault();
private Set<Class<?>> searched = new HashSet<Class<?>>(); // classes searched for translations
private Map<Class<?>, String> paths = new HashMap<Class<?>, String>(); // property file directory paths
private String helpURL = "http://www.opensourcephysics.org/online_help/tools/translator_tool_help.html"; //$NON-NLS-1$
private boolean keepHidden = false;
private XMLControl control = new XMLControlElement();
private XMLTable table;
private Class<?> classType;
private JPanel contentPane = new JPanel(new BorderLayout());
private String fileExtension;
private JLabel descriptionLabel;
private JComboBox localeDropDown;
private Icon saveIcon;
private JButton saveButton;
private JButton closeButton;
private JButton helpButton;
private String preferredTitle=null;
/**
* The singleton shared translator tool.
*/
private static final TranslatorTool TOOL = new TranslatorTool();
/**
* Gets the shared TranslatorTool.
*
* @return the shared TranslatorTool
*/
public static TranslatorTool getTool() {
return TOOL;
}
/**
* Shows the frame on the screen if the keep hidden flag is false.
*
* @deprecated
*/
public void show() {
if(!keepHidden) {
super.show();
}
}
/**
* Disposes all resources.
*/
public void dispose() {
keepHidden = true;
super.dispose();
}
/**
* Shows or hides this component depending on the value of parameter
* <code>b</code> and the <code>keepHidden</code> flag.
*
* OSP Applets often keep windows hidden.
*
* @param b
*/
public void setVisible(boolean b) {
if(!keepHidden) {
super.setVisible(b);
}
}
/**
* Sets the keepHidden flag.
*
* @param _keepHidden
*/
public void setKeepHidden(boolean _keepHidden) {
keepHidden = _keepHidden;
if(keepHidden) {
super.setVisible(false);
}
}
/**
* Reads the keepHidden flag.
*
*/
public boolean isKeepHidden() {
return keepHidden;
}
/**
* Private constructor.
*/
private TranslatorTool() {
if(OSPRuntime.appletMode) {
keepHidden = true;
}
String name = "TranslatorTool"; //$NON-NLS-1$
setName(name);
createGUI();
refreshGUI();
setLocale(ToolsRes.resourceLocale);
Toolbox.addTool(name, this);
}
/**
* Sends a job to this tool and specifies a tool to reply to.
*
* @param job the Job
* @param replyTo the tool to notify when the job is complete (may be null)
*/
public void send(Job job, Tool replyTo) {
/** not implemented */
}
/**
* Sets the locale.
*
* @param locale the locale
*/
public void setLocale(Locale locale) {
if(locale==this.locale) {
return;
}
this.locale = locale;
showProperties(classType);
// add new item if locale is not in dropdown
LocaleItem item = null;
if(localeDropDown!=null) {
// look for existing item
for(int i = 0; i<localeDropDown.getItemCount(); i++) {
item = (LocaleItem) localeDropDown.getItemAt(i);
if(item.loc.getLanguage().equals(locale.getLanguage())) {
break;
}
item = null;
}
// if not found, create and add new item
if(item==null) {
item = new LocaleItem(locale);
addDropDownItem(item);
}
localeDropDown.setSelectedItem(item);
// enable/disable save button
Map<String, String> properties = getProperties(classType, locale);
saveButton.setEnabled(changed.contains(properties));
// refresh objects associated with current class
refreshAssociates(classType);
}
}
/**
* Associates an object with a class for property lookup purposes.
*
* @param obj the object needing translations
* @param type the class
*/
public synchronized void associate(Object obj, Class<?> type) {
if(obj==null) {
return;
}
associates.put(obj, type);
}
/**
* Shows the properties for the specified class.
*
* @param type the class
*/
public void showProperties(Class<?> type) {
if(type==null) {
return;
}
classType = type;
// clear current values
control.clearValues();
// set file extension (language only)
fileExtension = ""; //$NON-NLS-1$
String addon = locale.getLanguage();
if(!addon.equals("")) { //$NON-NLS-1$
fileExtension += "_"+addon; //$NON-NLS-1$
}
fileExtension += ".properties"; //$NON-NLS-1$
// initialize control with default values
Collection<String> names = control.getPropertyNames();
Iterator<String> it = names.iterator();
while(it.hasNext()) {
String next = it.next();
control.setValue(next, next);
}
// set control values per current properties map
Map<String, String> properties = getProperties(type, locale);
Iterator<?> it2 = properties.keySet().iterator();
while(it2.hasNext()) {
String key = (String) it2.next();
control.setValue(key, properties.get(key));
}
// compare with defaults and flag unused properties
Set<String> keys = getDefaults(type).keySet();
it2 = properties.keySet().iterator();
while(it2.hasNext()) {
String key = (String) it2.next();
if(!keys.contains(key)) {
table.setBackgroundColor(key, Color.PINK);
}
}
table.refresh();
refreshGUI();
}
/**
* Sets a title for the tool
*/
public void setPreferredTitle(String title) {
preferredTitle = title;
refreshGUI();
}
/**
* Gets the localized value of a property for the specified class.
* If no localized value is found, the key is returned.
*
* @param type the class requesting the localized value
* @param key the string to localize
* @return the localized string
*/
public String getProperty(Class<?> type, String key) {
return getProperty(type, key, key, ToolsRes.resourceLocale);
}
/**
* Gets the localized value of a property for the specified class.
* If no localized value is found, the defaultValue is returned.
*
* @param type the class requesting the localized value
* @param key the string to localize
* @param defaultValue the default if no localized value found
* @return the localized string
*/
public String getProperty(Class<?> type, String key, String defaultValue) {
return getProperty(type, key, defaultValue, ToolsRes.resourceLocale);
}
/**
* Gets the localized value of a property for the specified object.
* The object must first be associated with a class.
* If no localized value is found, the key is returned.
*
* @param obj the object requesting the localized value
* @param key the string to localize
* @return the localized string
*/
public String getProperty(Object obj, String key) {
return getProperty(obj, key, key);
}
/**
* Gets the localized value of a property for the specified object.
* The object must first be associated with a class.
* If no localized value is found, the defaultValue is returned.
*
* @param obj the object requesting the localized value
* @param key the string to localize
* @param defaultValue the default if no localized value found
* @return the localized string
*/
public String getProperty(Object obj, String key, String defaultValue) {
if(obj==null) {
return(defaultValue==null) ? key : defaultValue;
}
Class<?> type = associates.get(obj);
return getProperty(type, key, defaultValue, ToolsRes.resourceLocale);
}
/**
* Removes a property from those defined for the specified class.
*
* @param type the class
* @param key the property to remove
*/
public void removeProperty(Class<?> type, String key) {
if(type==null) {
return;
}
// remove key from all properties maps for class type
getDefaults(type).remove(key);
Map<String, Map<String, String>> locales = classes.get(type);
if(locales!=null) {
Iterator<String> it = locales.keySet().iterator();
while(it.hasNext()) {
Map<String, String> properties = locales.get(it.next());
properties.remove(key);
flagChange(properties);
}
}
TOOL.showProperties(TOOL.classType);
refreshAssociates(TOOL.classType);
}
/**
* Removes a property from those defined for the specified object.
* The object must first be associated with a class.
*
* @param obj the object
* @param key the property to remove
*/
public void removeProperty(Object obj, String key) {
Class<?> type = associates.get(obj);
removeProperty(type, key);
}
/**
* Adds a property to those defined for the specified class.
*
* @param type the class
* @param key the property to add
* @param defaultValue the default value
*/
public void addProperty(Class<?> type, String key, String defaultValue) {
if((type==null)||(key==null)) {
return;
}
if(defaultValue==null) {
defaultValue = key;
}
// add key-value to all properties maps for class type
getDefaults(type).put(key, defaultValue);
Map<String, String> properties = getProperties(type, locale);
if(properties.get(key)==null) {
properties.put(key, defaultValue);
flagChange(properties);
}
Map<String, Map<String, String>> locales = classes.get(type);
if(locales!=null) {
Iterator<String> it = locales.keySet().iterator();
while(it.hasNext()) {
properties = locales.get(it.next());
if(properties.get(key)==null) {
properties.put(key, defaultValue);
flagChange(properties);
}
}
}
TOOL.showProperties(TOOL.classType);
// refresh objects associated with current class
refreshAssociates(TOOL.classType);
}
/**
* Gets objects associated with the specified class.
*/
public Collection<Object> getAssociates(Class<?> type) {
Collection<Object> c = new ArrayList<Object>();
Iterator<Object> it = associates.keySet().iterator();
while(it.hasNext()) {
Object obj = it.next();
if(associates.get(obj).equals(type)) {
c.add(obj);
}
}
return c;
}
/**
* Returns Locales for which translations exist for the specified class.
*/
public Locale[] getTranslatedLocales(Class<?> type) {
if(!searched.contains(type)) {
// search for and load all saved locales
synchronized(searched) {
searched.add(type);
}
Map<String, Map<String, String>> locales = classes.get(type);
if(locales==null) {
locales = new HashMap<String, Map<String, String>>();
synchronized(classes) {
classes.put(type, locales);
}
}
if(OSPRuntime.applet==null) { // search for languages if NOT an applet
Set<String> langs = locales.keySet(); // loaded language codes
String path = getPath(type);
Resource res = null;
String[] languages = Locale.getISOLanguages(); // all standard language codes
for(int i = 0; i<languages.length; i++) {
if(langs.contains(languages[i])) {
continue;
}
res = ResourceLoader.getResource(path+"_"+languages[i]+".properties"); //$NON-NLS-1$ //$NON-NLS-2$
if(res!=null) {
Map<String, String> properties = new TreeMap<String, String>();
locales.put(languages[i], properties);
// fill properties from resource
OSPLog.finer(res.getAbsolutePath());
readProperties(res.openReader(), properties);
}
}
}
}
Set<String> languages = new TreeSet<String>();
languages.addAll(classes.get(type).keySet()); // alphabetize
ArrayList<Locale> locales = new ArrayList<Locale>();
for(Iterator<String> it = languages.iterator(); it.hasNext(); ) {
locales.add(new Locale(it.next().toString()));
}
return locales.toArray(new Locale[0]);
}
/**
* Returns true if a String is a valid 2-letter language code.
*
* @param lang the 2-letter code
* @return true if valid 2-letter language code
*/
protected boolean isLanguage(String lang) {
String[] languages = Locale.getISOLanguages(); // all standard language codes
for(int i = 0; i<languages.length; i++) {
if(languages[i].equals(lang)) {
return true;
}
}
return false;
}
// ______________________________ private methods _____________________________
private void addDropDownItem(LocaleItem item) {
// sort items for dropdown: default first, then alphabetized
Map<String, LocaleItem> items = new TreeMap<String, LocaleItem>();
LocaleItem defaultItem = item.isDefault() ? item : null;
if(!item.isDefault()) {
items.put(item.language.toLowerCase(), item);
}
for(int i = 0; i<localeDropDown.getItemCount(); i++) {
LocaleItem next = (LocaleItem) localeDropDown.getItemAt(i);
if(next.isDefault()) {
defaultItem = next;
} else {
items.put(next.language.toLowerCase(), next);
}
}
localeDropDown.removeAllItems();
if(defaultItem!=null) {
localeDropDown.addItem(defaultItem);
}
for(Iterator<String> it = items.keySet().iterator(); it.hasNext(); ) {
localeDropDown.addItem(items.get(it.next()));
}
}
/**
* Gets the localized value of a property.
*
* @param type the class requesting the localized value
* @param key the string to localize
* @param defaultValue the default if no localized value found
* @param locale the locale
* @return the localized string
*/
private synchronized String getProperty(Class<?> type, String key, String defaultValue, Locale locale) {
if(defaultValue==null) {
defaultValue = key;
}
if(type==null) {
return defaultValue;
}
if(!getDefaults(type).keySet().contains(key)) {
addProperty(type, key, defaultValue);
}
return getProperties(type, locale).get(key);
}
/**
* Gets the properties file path for a class.
*
* @param type the class
* @return the path
*/
public String getPath(Class<?> type) {
if(type==null) {
return null;
}
String path = paths.get(type);
if (path!=null) { // saved path always ends with forward slash
return path+type.getSimpleName();
}
path = type.getName();
// replace all "." with "/"
int i = path.indexOf("."); //$NON-NLS-1$
while(i!=-1) {
path = path.substring(0, i)+"/"+path.substring(i+1); //$NON-NLS-1$
i = path.indexOf("."); //$NON-NLS-1$
}
return path;
}
/**
* Sets the path for a given class.
*
* @param type the class
* @param directory the path
*/
public void setPath(Class<?> type, String directory) {
directory = XML.forwardSlash(directory);
// add trailing slash, if none
if (!directory.endsWith("/")) //$NON-NLS-1$
directory += "/"; //$NON-NLS-1$
paths.put(type, directory);
}
/**
* Gets the default properties for the specified class.
*
* @param type the class type
* @return the default property-value map
*/
private Map<String, String> getDefaults(Class<?> type) {
Map<String, String> defaults = defaultProps.get(type);
if(defaults==null) {
defaults = new TreeMap<String, String>();
synchronized(defaultProps) {
defaultProps.put(type, defaults);
}
}
return defaults;
}
/**
* Gets the properties for the specified class and locale.
*
* @param type the class
* @param locale the locale
* @return the properties map <String key, String value>
*/
private Map<String, String> getProperties(Class<?> type, Locale locale) {
// look for properties map by class and language name
Map<String, Map<String, String>> locales = classes.get(type);
if(locales==null) {
locales = new HashMap<String, Map<String, String>>();
synchronized(classes) {
classes.put(type, locales);
}
}
Map<String, String> properties = locales.get(locale.getLanguage());
if(properties==null) {
properties = new TreeMap<String, String>();
locales.put(locale.getLanguage(), properties);
// fill properties from resource, if any
String path = getPath(type);
Resource res = null;
String lang = locale.getLanguage();
if(!lang.equals("")) { //$NON-NLS-1$
res = ResourceLoader.getResource(path+"_"+lang+".properties"); //$NON-NLS-1$ //$NON-NLS-2$
}
if(res==null) {
res = ResourceLoader.getResource(path+".properties"); //$NON-NLS-1$
}
if(res!=null) {
OSPLog.finer(res.getAbsolutePath());
readProperties(res.openReader(), properties);
} else {
// load properties with default values
Map<String, String> defaults = getDefaults(type);
Iterator<String> it = defaults.keySet().iterator();
while(it.hasNext()) {
String key = it.next();
String val = defaults.get(key);
properties.put(key, val);
}
// add properties to changed set
flagChange(properties);
}
}
return properties;
}
/**
* Reads the properties into the specified map.
*
* @param input the input reader
* @param map the map
*/
private void readProperties(BufferedReader input, Map<String, String> map) {
try {
// read properties line by line and put entries into the map
String next = input.readLine();
while(next!=null) {
int i = next.indexOf("="); //$NON-NLS-1$
if(i>-1) {
String key = next.substring(0, i);
String val = next.substring(i+1);
map.put(key, val);
}
next = input.readLine();
}
} catch(IOException ex) {
return;
}
}
/**
* Saves the current properties to the specified file.
*
* @param fileName the file name
* @return the name of the saved file, or null if not saved
*/
private String save(String fileName) {
if((fileName==null)||fileName.equals("")) { //$NON-NLS-1$
return null;
}
int n = fileName.lastIndexOf("/"); //$NON-NLS-1$
if(n<0) {
n = fileName.lastIndexOf("\\"); //$NON-NLS-1$
}
if(n>0) {
String dir = fileName.substring(0, n+1);
File file = new File(dir);
if(!file.exists()) {
XML.createFolders(dir);
}
if(!file.exists()) {
return null;
}
}
// assemble the properties
StringBuffer content = new StringBuffer();
String s = XML.stripExtension(fileName);
content.append("# This is the "+s+".properties file"+XML.NEW_LINE+XML.NEW_LINE); //$NON-NLS-1$ //$NON-NLS-2$
Iterator<String> it = control.getPropertyNames().iterator();
while(it.hasNext()) {
String key = it.next();
String alias = control.getString(key);
content.append(key+"="+alias+XML.NEW_LINE); //$NON-NLS-1$
}
File file = new File(fileName);
try {
if(file.exists()&&!file.canWrite()) {
JOptionPane.showMessageDialog(null,
ControlsRes.getString("Dialog.ReadOnly.Message"), //$NON-NLS-1$
ControlsRes.getString("Dialog.ReadOnly.Title"), //$NON-NLS-1$
JOptionPane.PLAIN_MESSAGE);
return null;
}
FileOutputStream stream = new FileOutputStream(file);
java.nio.charset.Charset charset = java.nio.charset.Charset.forName("UTF-8"); //$NON-NLS-1$
Writer out = new OutputStreamWriter(stream, charset);
out = new BufferedWriter(out);
out.write(content.toString());
out.flush();
out.close();
if(file.exists()) {
OSPLog.finest(file.getAbsolutePath());
synchronized(changed) {
changed.remove(getProperties(classType, locale));
}
// disable save button
saveButton.setEnabled(false);
return file.getAbsolutePath();
}
} catch(IOException ex) {
OSPLog.warning(ex.getMessage());
}
return null;
}
/**
* Creates the GUI.
*/
private void createGUI() {
XMLTableModel model = new XMLTableModel(control) {
public String getColumnName(int column) {
return(column==0) ? ToolsRes.getString("TranslatorTool.ColumnTitle.Property") : //$NON-NLS-1$
ToolsRes.getString("TranslatorTool.ColumnTitle.PropValue"); //$NON-NLS-1$
}
};
// configure the frame
contentPane.setPreferredSize(dim);
setContentPane(contentPane);
setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
// create the XMLTable
table = new XMLTable(model);
table.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
if(OSPRuntime.isPopupTrigger(e)) {
// find clicked row
for(int i = 0; i<table.getRowCount(); i++) {
Rectangle rect = table.getCellRect(i, 0, true);
if(rect.contains(e.getX(), e.getY())) {
table.setRowSelectionInterval(i, i);
final String name = (String) table.getValueAt(i, 0);
// show popup menu
JPopupMenu popup = new JPopupMenu();
JMenuItem removeItem = new JMenuItem(ToolsRes.getString("TranslatorTool.Popup.MenuItem.Remove") //$NON-NLS-1$
+" \""+name+"\""); //$NON-NLS-1$ //$NON-NLS-2$
popup.add(removeItem);
removeItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
removeProperty(classType, name);
}
});
popup.show(table, e.getX(), e.getY()+8);
}
}
}
}
});
JToolBar toolbar = new JToolBar();
toolbar.setFloatable(false);
contentPane.add(toolbar, BorderLayout.NORTH);
JScrollPane tableScroller = new JScrollPane(table);
contentPane.add(tableScroller, BorderLayout.CENTER);
JToolBar buttonbar = new JToolBar();
buttonbar.setFloatable(false);
contentPane.add(buttonbar, BorderLayout.SOUTH);
descriptionLabel = new JLabel();
descriptionLabel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 6));
toolbar.add(descriptionLabel);
// assemble locale dropdown
localeDropDown = new JComboBox();
LocaleItem selectedItem = new LocaleItem(locale);
localeDropDown.addItem(selectedItem);
Locale[] locales = OSPRuntime.getInstalledLocales();
for(int i = 0; i<locales.length; i++) {
if(locales[i].getDisplayLanguage().equals(locale.getDisplayLanguage())) {
continue;
}
addDropDownItem(new LocaleItem(locales[i]));
}
localeDropDown.setSelectedItem(selectedItem);
localeDropDown.setEditable(true);
localeDropDown.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Object next = localeDropDown.getSelectedItem();
if(next==null) {
return;
}
if(next instanceof LocaleItem) {
ToolsRes.setLocale(((LocaleItem) next).loc);
} else if(isLanguage(next.toString())) {
String lang = new Locale(next.toString()).getLanguage();
// select existing LocaleItem, if any
Locale locale = null;
LocaleItem item = null;
for(int i = 0; i<localeDropDown.getItemCount(); i++) {
item = (LocaleItem) localeDropDown.getItemAt(i);
if(lang.equals(item.loc.getLanguage())) {
locale = item.loc;
break;
}
item = null;
}
if(locale==null) {
locale = new Locale(next.toString());
Map<String, String> properties = getProperties(classType, locale);
flagChange(properties);
}
ToolsRes.setLocale(locale);
if(item!=null) {
localeDropDown.setSelectedItem(item);
}
localeDropDown.getEditor().selectAll();
} else {
localeDropDown.setSelectedIndex(0);
localeDropDown.getEditor().selectAll();
}
}
});
toolbar.add(localeDropDown);
// create buttons
helpButton = new JButton();
helpButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
TextFrame frame;
// show helpURL if available
if(ResourceLoader.getResource(helpURL)!=null) {
frame = new TextFrame(helpURL);
} else {
String htmlFile = "/org/opensourcephysics/resources/tools/html/translator_tool_help.html"; //$NON-NLS-1$
frame = new TextFrame(htmlFile);
}
frame.setSize(800, 600);
frame.setVisible(true);
}
});
buttonbar.add(helpButton);
String imageFile = "/org/opensourcephysics/resources/tools/images/save.gif"; //$NON-NLS-1$
saveIcon = ResourceLoader.getIcon(imageFile);
saveButton = new JButton(saveIcon);
saveButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
save(getPath(classType)+fileExtension);
}
});
saveButton.setEnabled(false);
buttonbar.add(Box.createHorizontalGlue());
buttonbar.add(saveButton);
closeButton = new JButton();
closeButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setVisible(false);
}
});
buttonbar.add(closeButton);
pack();
// center this on the screen
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
int x = (dim.width-getBounds().width)/2;
int y = (dim.height-getBounds().height)/2;
setLocation(x, y);
// listen to ToolsRes for locale changes
ToolsRes.addPropertyChangeListener("locale", new PropertyChangeListener() { //$NON-NLS-1$
public void propertyChange(PropertyChangeEvent e) {
Locale locale = (Locale) e.getNewValue();
if(locale!=null) {
setLocale(locale);
}
}
});
table.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
// update current properties map
Object val = e.getNewValue();
if(val instanceof TableModelEvent) {
TableModelEvent event = (TableModelEvent) val;
int row = event.getFirstRow();
if(row<0) {
return;
}
Map<String, String> properties = getProperties(classType, locale);
String key = (String) table.getValueAt(row, 0);
String alias = (String) table.getValueAt(row, 1);
if((alias!=null)&&!alias.equals("")) { //$NON-NLS-1$
properties.put(key, alias);
} else {
table.setValueAt(key, row, 1);
properties.put(key, key);
}
// refresh objects associated with current class
refreshAssociates(classType);
// add properties to changed set
flagChange(properties);
// enable save button
saveButton.setEnabled(true);
}
}
});
}
/**
* Refreshes the GUI.
*/
protected void refreshGUI() {
String fileName = XML.getName(getPath(classType));
if (preferredTitle==null) {
String title = ToolsRes.getString("TranslatorTool.Title"); //$NON-NLS-1$
title += " "+fileName; //$NON-NLS-1$
setTitle(title);
}
else setTitle(preferredTitle);
fileName += fileExtension;
if(classType!=null) {
Map<String, String> properties = getProperties(classType, locale);
saveButton.setEnabled(changed.contains(properties));
}
helpButton.setText(ToolsRes.getString("Tool.Button.Help")); //$NON-NLS-1$
helpButton.setToolTipText(ToolsRes.getString("Tool.Button.Help.ToolTip")); //$NON-NLS-1$
saveButton.setText(ToolsRes.getString("TranslatorTool.Button.Save")); //$NON-NLS-1$
saveButton.setToolTipText(ToolsRes.getString("TranslatorTool.Button.Save.ToolTip")+fileName); //$NON-NLS-1$
closeButton.setText(ToolsRes.getString("Tool.Button.Close")); //$NON-NLS-1$
closeButton.setToolTipText(ToolsRes.getString("Tool.Button.Close.ToolTip")); //$NON-NLS-1$
descriptionLabel.setText(ToolsRes.getString("TranslatorTool.Label.Description")); //$NON-NLS-1$
table.refresh();
}
/**
* Refreshes objects associated with the specified class.
*/
protected void refreshAssociates(Class<?> type) {
Iterator<Object> it = getAssociates(type).iterator();
while(it.hasNext()) {
Object obj = it.next();
if(obj instanceof XMLTable) {
((XMLTable) obj).refresh();
} else if(obj instanceof PropertyChangeListener) {
((PropertyChangeListener) obj).propertyChange(new java.beans.PropertyChangeEvent(TOOL, "translation", null, null)); //$NON-NLS-1$
}
}
}
private synchronized void flagChange(Map<String, String> properties) {
synchronized(changed) {
changed.add(properties);
}
}
/**
* A class to display languages in a JComboBox.
*/
private class LocaleItem {
Locale loc;
String language;
LocaleItem(Locale locale) {
loc = locale;
language = OSPRuntime.getDisplayLanguage(loc);
if(isDefault()) {
language += " ("+ToolsRes.getString("TranslatorTool.Language.Default")+")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
public String toString() {
return language;
}
public boolean isDefault() {
return loc.getDisplayLanguage(loc).equals(Locale.getDefault().getDisplayLanguage(loc));
}
}
}
/*
* Open Source Physics software is free software; you can redistribute
* it and/or modify it under the terms of the GNU General Public License (GPL) as
* published by the Free Software Foundation; either version 2 of the License,
* or(at your option) any later version.
*
* Code that uses any portion of the code in the org.opensourcephysics package
* or any subpackage (subdirectory) of this package must must also be be released
* under the GNU GPL license.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA
* or view the license online at http://www.gnu.org/copyleft/gpl.html
*
* Copyright (c) 2007 The Open Source Physics project
* http://www.opensourcephysics.org
*/