commit
2f68f7abee
|
@ -15,6 +15,8 @@ Since Tengwar is inherently a phonetic script, converting english to Tengwar wou
|
|||
|
||||
![Tengwar](https://puu.sh/vtyfi/598ad704e1.png "Tengwar Characters")
|
||||
|
||||
To translate from Tengwar back to English is much simpler, but because Tengwar characters do use capitalization, the resulting string of text will be in only lower-case letters.
|
||||
|
||||
### To-Do List
|
||||
|
||||
* Implement s-curls.
|
||||
|
|
|
@ -4,10 +4,8 @@ import javax.swing.*;
|
|||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author Andrew Lalis
|
||||
|
@ -15,8 +13,8 @@ import java.io.IOException;
|
|||
*/
|
||||
public class Main {
|
||||
|
||||
public static final String TITLE = "Tengwar Typewriter";
|
||||
public static final String ICON_PATH = "resources/icon.png";
|
||||
private static final String TITLE = "Tengwar Typewriter";
|
||||
private static final String ICON_PATH = "resources/icon.png";
|
||||
|
||||
public static void main(String[] args){
|
||||
JFrame f = new JFrame(TITLE);
|
||||
|
@ -31,31 +29,16 @@ public class Main {
|
|||
JMenu fileMenu = new JMenu("File");
|
||||
//Save Item.
|
||||
JMenuItem saveItem = new JMenuItem("Save");
|
||||
saveItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK));
|
||||
saveItem.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
window.onSaveClicked();
|
||||
}
|
||||
});
|
||||
saveItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK));
|
||||
saveItem.addActionListener(e -> window.onSaveClicked());
|
||||
fileMenu.add(saveItem);
|
||||
//Import Item.
|
||||
JMenuItem importItem = new JMenuItem("Import");
|
||||
importItem.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
window.onImportClicked();
|
||||
}
|
||||
});
|
||||
importItem.addActionListener(e -> window.onImportClicked());
|
||||
fileMenu.add(importItem);
|
||||
//Exit Item.
|
||||
JMenuItem exitItem = new JMenuItem("Exit");
|
||||
exitItem.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
f.dispose();
|
||||
}
|
||||
});
|
||||
exitItem.addActionListener(e -> f.dispose());
|
||||
fileMenu.add(exitItem);
|
||||
menuBar.add(fileMenu);
|
||||
//Edit menu.
|
||||
|
@ -63,49 +46,29 @@ public class Main {
|
|||
//Live checkbox
|
||||
JCheckBoxMenuItem liveCheckBox = new JCheckBoxMenuItem("Live");
|
||||
liveCheckBox.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L, ActionEvent.CTRL_MASK));
|
||||
liveCheckBox.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
window.onLiveToggled(e);
|
||||
}
|
||||
});
|
||||
liveCheckBox.addActionListener(e -> window.onLiveToggled(e));
|
||||
editMenu.add(liveCheckBox);
|
||||
menuBar.add(editMenu);
|
||||
//About Menu.
|
||||
JMenu aboutMenu = new JMenu("About");
|
||||
//Tengwar Item.
|
||||
JMenuItem tengwarItem = new JMenuItem("Tengwar");
|
||||
tengwarItem.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
window.onTengwarAboutClicked();
|
||||
}
|
||||
});
|
||||
tengwarItem.addActionListener(e -> window.onTengwarAboutClicked());
|
||||
aboutMenu.add(tengwarItem);
|
||||
//English Mode.
|
||||
JMenuItem englishModeItem = new JMenuItem("English Mode");
|
||||
englishModeItem.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
window.onEnglishModeAboutClicked();
|
||||
}
|
||||
});
|
||||
englishModeItem.addActionListener(e -> window.onEnglishModeAboutClicked());
|
||||
aboutMenu.add(englishModeItem);
|
||||
//About the author.
|
||||
JMenuItem aboutMeItem = new JMenuItem("About the Author");
|
||||
aboutMeItem.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
window.onAboutMeClicked();
|
||||
}
|
||||
});
|
||||
aboutMeItem.addActionListener(e -> window.onAboutMeClicked());
|
||||
aboutMenu.add(aboutMeItem);
|
||||
menuBar.add(aboutMenu);
|
||||
|
||||
|
||||
f.setJMenuBar(menuBar);
|
||||
f.pack();
|
||||
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) {
|
||||
|
|
|
@ -11,13 +11,13 @@ import java.awt.event.*;
|
|||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Window {
|
||||
|
||||
public static final List<String> OPEN_FILE_EXTENSIONS = new ArrayList<>();
|
||||
//List of files that can be imported.
|
||||
private static final List<String> OPEN_FILE_EXTENSIONS = new ArrayList<>();
|
||||
|
||||
static {
|
||||
OPEN_FILE_EXTENSIONS.add("txt");
|
||||
|
@ -39,40 +39,28 @@ public class Window {
|
|||
|
||||
private boolean isLive = false;
|
||||
|
||||
public Window() {
|
||||
|
||||
Window() {
|
||||
//Load the font into the system.
|
||||
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
Font tengwar_annatar = null;
|
||||
try {
|
||||
tengwar_annatar = Font.createFont(Font.TRUETYPE_FONT, this.getClass().getClassLoader().getResourceAsStream("resources/tngan.ttf"));
|
||||
ge.registerFont(Font.createFont(Font.TRUETYPE_FONT, this.getClass().getClassLoader().getResourceAsStream("resources/tngan.ttf")));
|
||||
} catch (FontFormatException e) {
|
||||
e.printStackTrace();
|
||||
System.err.println("Font format exception!");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
System.err.println("IOException!");
|
||||
}
|
||||
ge.registerFont(tengwar_annatar);
|
||||
|
||||
//Explicit translation from english to tengwar.
|
||||
toTengwarButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
tengwarTextArea.setText(Translator.translateToTengwar(inputTextArea.getText()));
|
||||
}
|
||||
});
|
||||
toTengwarButton.addActionListener(e -> tengwarTextArea.setText(Translator.translateToTengwar(inputTextArea.getText())));
|
||||
|
||||
//Clear both text areas.
|
||||
clearButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
clearButton.addActionListener(e -> {
|
||||
tengwarTextArea.setText(null);
|
||||
inputTextArea.setText(null);
|
||||
}
|
||||
});
|
||||
|
||||
//Translate tengwar to english.
|
||||
toEnglishButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
}
|
||||
});
|
||||
toEnglishButton.addActionListener(e -> inputTextArea.setText(Translator.translateToEnglish(tengwarTextArea.getText())));
|
||||
}
|
||||
|
||||
private void createUIComponents() {
|
||||
|
@ -95,26 +83,53 @@ public class Window {
|
|||
}
|
||||
});
|
||||
this.tengwarTextArea = new JTextArea();
|
||||
tengwarTextArea.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
onTengwarTextChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
onTengwarTextChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
onTengwarTextChanged();
|
||||
}
|
||||
});
|
||||
this.toTengwarButton = new JButton();
|
||||
this.toEnglishButton = new JButton();
|
||||
this.clearButton = new JButton();
|
||||
this.inputTextArea.requestFocus();
|
||||
}
|
||||
|
||||
public JPanel getMainPanel(){
|
||||
JPanel getMainPanel(){
|
||||
return this.mainPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the english text is changed, and live is selected, then update the tengwar text.
|
||||
*/
|
||||
private void onEnglishTextChanged(){
|
||||
if (isLive)
|
||||
if (isLive && inputTextArea.hasFocus())
|
||||
tengwarTextArea.setText(Translator.translateToTengwar(inputTextArea.getText()));
|
||||
}
|
||||
|
||||
/**
|
||||
* If tengwar text is changed, and live is selected, then update the english text.
|
||||
*/
|
||||
private void onTengwarTextChanged(){
|
||||
if (isLive && tengwarTextArea.hasFocus())
|
||||
inputTextArea.setText(Translator.translateToEnglish(tengwarTextArea.getText()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the live button and disable translate buttons if needed.
|
||||
* @param e event generated by the check box.
|
||||
*/
|
||||
public void onLiveToggled(ActionEvent e){
|
||||
void onLiveToggled(ActionEvent e){
|
||||
if (((JCheckBoxMenuItem) e.getSource()).getState()){
|
||||
//Deactivate buttons, set live to true.
|
||||
toTengwarButton.setEnabled(false);
|
||||
|
@ -131,7 +146,7 @@ public class Window {
|
|||
/**
|
||||
* What to do if the user clicks the 'import' button.
|
||||
*/
|
||||
public void onImportClicked(){
|
||||
void onImportClicked(){
|
||||
JFileChooser fileChooser = new JFileChooser(System.getProperty("user.documents"));
|
||||
fileChooser.setFileFilter(new FileFilter() {
|
||||
@Override
|
||||
|
@ -139,10 +154,7 @@ public class Window {
|
|||
if (f.isDirectory())
|
||||
return true;
|
||||
String extension = Utils.getExtension(f);
|
||||
if (extension != null)
|
||||
return OPEN_FILE_EXTENSIONS.contains(extension.toLowerCase());
|
||||
else
|
||||
return false;
|
||||
return extension != null && OPEN_FILE_EXTENSIONS.contains(extension.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -171,7 +183,7 @@ public class Window {
|
|||
/**
|
||||
* What to do when the user wants to save their document.
|
||||
*/
|
||||
public void onSaveClicked(){
|
||||
void onSaveClicked(){
|
||||
JFileChooser fileChooser = new JFileChooser(System.getProperty("user.documents"));
|
||||
int result = fileChooser.showSaveDialog(this.mainPanel);
|
||||
if (result == JFileChooser.APPROVE_OPTION){
|
||||
|
@ -186,7 +198,7 @@ public class Window {
|
|||
/**
|
||||
* Attempt to open the pdf in the browser.
|
||||
*/
|
||||
public void onEnglishModeAboutClicked(){
|
||||
void onEnglishModeAboutClicked(){
|
||||
if (Desktop.isDesktopSupported()){
|
||||
try{
|
||||
Desktop.getDesktop().browse(new URI("https://github.com/andrewlalis/TengwarTranslator/blob/master/src/resources/EnglishOneToOneTengwarV2-1.pdf"));
|
||||
|
@ -199,7 +211,7 @@ public class Window {
|
|||
/**
|
||||
* Attempt to open Tengwar wikipedia page.
|
||||
*/
|
||||
public void onTengwarAboutClicked(){
|
||||
void onTengwarAboutClicked(){
|
||||
if (Desktop.isDesktopSupported()){
|
||||
try{
|
||||
Desktop.getDesktop().browse(new URI("https://en.wikipedia.org/wiki/Tengwar"));
|
||||
|
@ -212,7 +224,7 @@ public class Window {
|
|||
/**
|
||||
* Attempt to open my github page.
|
||||
*/
|
||||
public void onAboutMeClicked(){
|
||||
void onAboutMeClicked(){
|
||||
if (Desktop.isDesktopSupported()){
|
||||
try{
|
||||
Desktop.getDesktop().browse(new URI("https://github.com/andrewlalis"));
|
||||
|
|
|
@ -1,28 +1,12 @@
|
|||
package net.agspace.translate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Created by Andrew's Computer on 23-Apr-17.
|
||||
*/
|
||||
public class Translator {
|
||||
|
||||
private enum CHAR_TYPE {
|
||||
CONSONANT,
|
||||
COMPOUND,
|
||||
ALTERNATE,
|
||||
VOWEL,
|
||||
NUMBER,
|
||||
CARRIER,
|
||||
PUNCTUATION,
|
||||
BAR,
|
||||
S_CURL,
|
||||
UNKNOWN,
|
||||
}
|
||||
|
||||
//Consonant variables.
|
||||
private static final char B = 'w';
|
||||
private static final char C = 'c';
|
||||
|
@ -184,7 +168,6 @@ public class Translator {
|
|||
alternateChars.put('s', S_ALT);
|
||||
alternateChars.put('z', Z_ALT);
|
||||
alternateChars.put('w', W_ALT);
|
||||
//alternateChars.put('r', R_ALT); R alt is used if followed by a vowel.
|
||||
alternateChars.put('y', Y_ALT);
|
||||
}
|
||||
|
||||
|
@ -372,6 +355,92 @@ public class Translator {
|
|||
return (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a tengwar character is a vowel.
|
||||
* @param c tengwar char to check.
|
||||
* @return true if vowel, false if not.
|
||||
*/
|
||||
private static boolean isVowelTengwar(char c){
|
||||
for (Character[] ch : vowelChars.values()){
|
||||
for (char cha : ch){
|
||||
if (cha == c)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the english vowel representation of a tengwar vowel.
|
||||
* @param tengwarVowel The tengwar vowel to decipher.
|
||||
* @return english vowel char.
|
||||
*/
|
||||
private static char getEnglishVowel(char tengwarVowel){
|
||||
if (!isVowelTengwar(tengwarVowel)){
|
||||
return 0;
|
||||
}
|
||||
for (Character[] ch : vowelChars.values()){
|
||||
for (char cha : ch){
|
||||
if (cha == tengwarVowel) {
|
||||
return getKeyByValue(vowelChars, ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a tengwar char is a silent E.
|
||||
* @param tengwarChar the tengwar char to evaluate.
|
||||
* @return True if it is a silent E, false otherwise.
|
||||
*/
|
||||
private static boolean isSilentE(char tengwarChar){
|
||||
return tengwarChar == E_UNDER_1
|
||||
|| tengwarChar == E_UNDER_2
|
||||
|| tengwarChar == E_UNDER_3
|
||||
|| tengwarChar == E_UNDER_4
|
||||
|| tengwarChar == E_LAMBE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a character or string that is a literal translation from tengwar.
|
||||
* @param tengwarChar the tengwar char to translate.
|
||||
* @return english representation of the tengwar.
|
||||
*/
|
||||
private static String getEnglishLiteral(char tengwarChar){
|
||||
if (consonantChars.containsValue(tengwarChar)){
|
||||
return getKeyByValue(consonantChars, tengwarChar).toString();
|
||||
} else if (compoundChars.containsValue(tengwarChar)){
|
||||
return getKeyByValue(compoundChars, tengwarChar);
|
||||
} else if (alternateChars.containsValue(tengwarChar)) {
|
||||
return getKeyByValue(alternateChars, tengwarChar).toString();
|
||||
} else if (tengwarChar == R_ALT){
|
||||
return "r";
|
||||
} else if (numberChars.containsValue(tengwarChar)){
|
||||
return getKeyByValue(numberChars, tengwarChar).toString();
|
||||
} else if (punctuationChars.containsValue(tengwarChar)){
|
||||
return getKeyByValue(punctuationChars, tengwarChar).toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key for a specified value in a 1 to 1 map.
|
||||
* @param map The 1 to 1 map to use.
|
||||
* @param value Value to return the key of.
|
||||
* @param <T> Key type of the map.
|
||||
* @param <E> Value type of the map.
|
||||
* @return Key associated with value.
|
||||
*/
|
||||
private static <T, E> T getKeyByValue(Map<T, E> map, E value) {
|
||||
for (Map.Entry<T, E> entry : map.entrySet()) {
|
||||
if (Objects.equals(value, entry.getValue())) {
|
||||
return entry.getKey();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate an english string to tengwar.
|
||||
* @param englishString english string.
|
||||
|
@ -380,19 +449,14 @@ public class Translator {
|
|||
public static String translateToTengwar(String englishString){
|
||||
StringBuilder result = new StringBuilder(englishString.length());
|
||||
String input = englishString.toLowerCase().trim();
|
||||
|
||||
for (int i = 0; i < input.length(); i++){
|
||||
//Attempt to get previous, current, and next char.
|
||||
char currentChar = input.charAt(i);
|
||||
char previousChar = 0;
|
||||
char nextChar = 0;
|
||||
try{
|
||||
previousChar = input.charAt(i-1);
|
||||
} catch (IndexOutOfBoundsException e){
|
||||
}
|
||||
try{
|
||||
nextChar = input.charAt(i+1);
|
||||
} catch (IndexOutOfBoundsException e){
|
||||
//Do nothing, since this may happen.
|
||||
}
|
||||
//Test if the current char is a vowel.
|
||||
if (isVowel(currentChar)){
|
||||
|
@ -429,6 +493,7 @@ public class Translator {
|
|||
}
|
||||
result.append(tengwarCharToBeAdded);
|
||||
result.append(getAppropriateVowel(tengwarCharToBeAdded, currentChar));
|
||||
//Bar gets added after the vowel.
|
||||
if (needsBar)
|
||||
result.append(bars.get(barSizes.get(tengwarCharToBeAdded)));
|
||||
i++;
|
||||
|
@ -483,4 +548,60 @@ public class Translator {
|
|||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a tengwar string to english.
|
||||
* @param tengwarString tengwar string.
|
||||
* @return english string.
|
||||
*/
|
||||
public static String translateToEnglish(String tengwarString){
|
||||
StringBuilder result = new StringBuilder(tengwarString.length()+tengwarString.length()/2);
|
||||
for (int i = 0; i < tengwarString.length(); i++){
|
||||
char currentChar = tengwarString.charAt(i);
|
||||
char nextChar = 0;
|
||||
char secondNextChar = 0;
|
||||
try{
|
||||
nextChar = tengwarString.charAt(i+1);
|
||||
} catch (IndexOutOfBoundsException e){
|
||||
//Do nothing, this is fine.
|
||||
}
|
||||
try{
|
||||
secondNextChar = tengwarString.charAt(i+2);
|
||||
} catch (IndexOutOfBoundsException e){
|
||||
//Do nothing, this is fine.
|
||||
}
|
||||
String currentLiteral = getEnglishLiteral(currentChar);
|
||||
//Check if the current character is a literal translation.
|
||||
if (currentLiteral != null){
|
||||
//Check if the next character is a vowel that should be placed before a character.
|
||||
if (isVowelTengwar(nextChar) && !isSilentE(nextChar)){
|
||||
result.append(getEnglishVowel(nextChar));
|
||||
result.append(currentLiteral);
|
||||
i++;
|
||||
//Check for a double-bar, and then add whatever the current char is.
|
||||
if (bars.contains(secondNextChar)){
|
||||
result.append(currentLiteral);
|
||||
i++;
|
||||
}
|
||||
} else if (bars.contains(nextChar)){
|
||||
result.append(currentLiteral);
|
||||
i++;
|
||||
} else {
|
||||
//Just append the current literal, no skipping.
|
||||
result.append(currentLiteral);
|
||||
}
|
||||
} else {
|
||||
//Current character is not a literal translation, so it is an E_UNDER, or carrier.
|
||||
if (carriers.contains(currentChar)){
|
||||
//If carrier, append the following vowel.
|
||||
result.append(getEnglishVowel(nextChar));
|
||||
i++;
|
||||
} else if (isSilentE(currentChar)) {
|
||||
result.append('e');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue