diff --git a/pom.xml b/pom.xml index 044a60de..edfc4d7c 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,12 @@ javafx-swing 17.0.2 + + + org.openjfx + javafx-web + 17.0.2 + org.apache.commons @@ -78,6 +84,12 @@ 2.6 + + org.xerial + sqlite-jdbc + 3.40.0.0 + + org.junit.jupiter junit-jupiter diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 692fe1ad..edf8b103 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -4,8 +4,10 @@ requires javafx.controls; requires javafx.media; requires javafx.swing; + requires javafx.web; requires java.desktop; requires java.logging; + requires java.sql; requires java.xml; requires jdk.charsets; requires jsr305; //AUT - fixed with module injector - only needed for jsoup diff --git a/src/main/java/org/darisadesigns/polyglotlina/CustHandlerFactory.java b/src/main/java/org/darisadesigns/polyglotlina/CustHandlerFactory.java index 08ecb6b6..533dff05 100644 --- a/src/main/java/org/darisadesigns/polyglotlina/CustHandlerFactory.java +++ b/src/main/java/org/darisadesigns/polyglotlina/CustHandlerFactory.java @@ -36,6 +36,7 @@ import org.darisadesigns.polyglotlina.CustomControls.GrammarSectionNode; import org.darisadesigns.polyglotlina.CustomControls.GrammarChapNode; import org.darisadesigns.polyglotlina.ManagersCollections.RomanizationManager; +import org.darisadesigns.polyglotlina.ManagersCollections.TranslationManager; import org.darisadesigns.polyglotlina.Nodes.EtyExternalParent; import org.darisadesigns.polyglotlina.Nodes.ToDoNode; import org.darisadesigns.polyglotlina.Nodes.WordClassValue; @@ -97,7 +98,7 @@ public static CustHandler getCustHandler(InputStream iStream, DictCore core) thr private static CustHandler get075orHigherHandler(final DictCore core, final int versionHierarchy) { return new CustHandler() { - private StringBuilder stringBuilder; + private StringBuilder stringBuilder = new StringBuilder(); PronunciationNode proBuffer; PronunciationNode romBuffer; String charRepCharBuffer = ""; @@ -236,7 +237,7 @@ private static CustHandler get075orHigherHandler(final DictCore core, final int @Override public void startElement(String uri, String localName, String qName, Attributes attributes) { - stringBuilder = new StringBuilder(); + stringBuilder.setLength(0); if (qName.equalsIgnoreCase(PGTUtil.DICTIONARY_SAVE_DATE)) { blastSave = true; @@ -493,6 +494,7 @@ public void startElement(String uri, String localName, String qName, Attributes @Override public void endElement(String uri, String localName, String qName) throws SAXException { + String builderValue = stringBuilder.toString(); if (qName.equalsIgnoreCase(PGTUtil.DICTIONARY_SAVE_DATE)) { blastSave = false; @@ -543,7 +545,7 @@ public void endElement(String uri, String localName, } else if (qName.equalsIgnoreCase(PGTUtil.WORD_RULEOVERRIDE_XID)) { bwordRuleOverride = false; } else if (qName.equalsIgnoreCase(PGTUtil.WORD_CLASS_AND_VALUE_XID)) { - String[] classValIds = stringBuilder.toString().split(","); + String[] classValIds = builderValue.split(","); int classId = Integer.parseInt(classValIds[0]); int valId = Integer.parseInt(classValIds[1]); core.getWordCollection().getBufferWord().setClassValue(classId, valId); @@ -866,13 +868,16 @@ public void endElement(String uri, String localName, procMan.addSyllable(tmpString); } else if (qName.equalsIgnoreCase(PGTUtil.PRO_GUIDE_COMPOSITION_SYLLABLE)) { bsyllableComposition = false; + } else if (qName.equalsIgnoreCase(TranslationManager.TMP_FILENAME_XID)) { + core.getTranslationManager().setTmpFileName(builderValue); } } @Override public void characters(char[] ch, int start, int length) throws SAXException { - + stringBuilder.append(ch, start, length); + if (blastSave) { core.setLastSaveTime(Instant.parse(new String(ch, start, length))); } else if (blocalWord) { @@ -907,7 +912,7 @@ public void characters(char[] ch, int start, int length) .setRulesOverride(new String(ch, start, length).equals(PGTUtil.TRUE)); bwordRuleOverride = false; } else if (bclassVal) { - stringBuilder.append(new String(ch, start, length)); + // stringBuilder.append(new String(ch, start, length)); } else if (bwordClassTextVal) { if (ruleIdBuffer == 0) { String[] classValIds = new String(ch, start, length).split(","); diff --git a/src/main/java/org/darisadesigns/polyglotlina/Desktop/DesktopIOHandler.java b/src/main/java/org/darisadesigns/polyglotlina/Desktop/DesktopIOHandler.java index 8eb14c58..e0a2f162 100644 --- a/src/main/java/org/darisadesigns/polyglotlina/Desktop/DesktopIOHandler.java +++ b/src/main/java/org/darisadesigns/polyglotlina/Desktop/DesktopIOHandler.java @@ -28,6 +28,7 @@ import org.darisadesigns.polyglotlina.ManagersCollections.ImageCollection; import org.darisadesigns.polyglotlina.Desktop.ManagersCollections.DesktopOptionsManager; import org.darisadesigns.polyglotlina.ManagersCollections.ReversionManager; +import org.darisadesigns.polyglotlina.ManagersCollections.TranslationManager; import org.darisadesigns.polyglotlina.Nodes.ImageNode; import org.darisadesigns.polyglotlina.Nodes.ReversionNode; import java.awt.Desktop; @@ -530,6 +531,7 @@ private String writeRawFileOutput(File tmpSaveLocation, byte[] xmlData, DictCore writeLog += writeImagesToArchive(out, core); writeLog += writeWavToArchive(out, core); writeLog += writePriorStatesToArchive(out, core); + writeLog += writeTranslationDBToArchive(out, core); out.finish(); } @@ -733,6 +735,24 @@ private String writeImagesToArchive(ZipOutputStream out, DictCore core) { return writeLog; } + private String writeTranslationDBToArchive(ZipOutputStream out, DictCore core) { + String writeLog = ""; + String tmpFileName = core.getTranslationManager().getTmpFileName(); + File dir = core.getOSHandler().getWorkingDirectory(); + String dbPath = ""; + try { + dbPath = dir.getCanonicalPath() + File.separator + tmpFileName; + out.putNextEntry(new ZipEntry(TranslationManager.ZIP_FILE_NAME)); + try (InputStream dbInput = new FileInputStream(dbPath)) { + dbInput.transferTo(out); + } + out.closeEntry(); + } catch (IOException e) { + writeLog = "Unable to save translations DB.\n"; + } + return writeLog; + } + /** * Tests whether a file at a particular location exists. Wrapped to avoid IO * code outside this file @@ -854,6 +874,24 @@ public void loadReversionStates(ReversionManager reversionManager, } } + @Override + public void loadTranslationDB(TranslationManager translationManager, String filePath) throws IOException { + ZipFile zipFile = new ZipFile(filePath); + try (zipFile) { + ZipEntry translationDB = zipFile.getEntry(TranslationManager.ZIP_FILE_NAME); + + if (null == translationDB) return; + + File dir = translationManager.getCore().getOSHandler().getWorkingDirectory(); + String dbPath = dir.getCanonicalPath() + File.separator + translationManager.getTmpFileName(); + OutputStream tmpFileStream = new FileOutputStream(dbPath); + try (tmpFileStream) { + zipFile.getInputStream(translationDB).transferTo(tmpFileStream); + translationManager.setIsInitialized(true); + } + } + } + /** * Exports font in PGD to external file * diff --git a/src/main/java/org/darisadesigns/polyglotlina/DictCore.java b/src/main/java/org/darisadesigns/polyglotlina/DictCore.java index d5ae5aee..bb21b3e1 100644 --- a/src/main/java/org/darisadesigns/polyglotlina/DictCore.java +++ b/src/main/java/org/darisadesigns/polyglotlina/DictCore.java @@ -34,6 +34,7 @@ import org.darisadesigns.polyglotlina.ManagersCollections.ReversionManager; import org.darisadesigns.polyglotlina.ManagersCollections.RomanizationManager; import org.darisadesigns.polyglotlina.ManagersCollections.ToDoManager; +import org.darisadesigns.polyglotlina.ManagersCollections.TranslationManager; import org.darisadesigns.polyglotlina.ManagersCollections.WordClassCollection; import org.darisadesigns.polyglotlina.OSHandler.CoreUpdatedListener; import org.darisadesigns.polyglotlina.OSHandler.FileReadListener; @@ -75,6 +76,7 @@ public class DictCore { private ReversionManager reversionManager; private PhraseManager phraseManager; private ToDoManager toDoManager; + private TranslationManager translationManager; private final OSHandler osHandler; private boolean curLoading = false; private Instant lastSaveTime = Instant.MIN; @@ -115,6 +117,7 @@ private void initializeDictCore(PropertiesManager _propertiesManager, GrammarMan reversionManager = new ReversionManager(this); toDoManager = new ToDoManager(); phraseManager = new PhraseManager(this); + translationManager = new TranslationManager(this); PAlphaMap alphaOrder = propertiesManager.getAlphaOrder(); subscribers = new ArrayList<>(); @@ -408,6 +411,13 @@ public void readFile(String _fileName, byte[] overrideXML, boolean useFileReadLi warningLog += e.getLocalizedMessage() + "\n"; } + try { + this.osHandler.getIOHandler().loadTranslationDB(translationManager, _fileName); + } catch (IOException e) { + this.osHandler.getIOHandler().writeErrorLog(e); + warningLog += e.getLocalizedMessage() + "\n"; + } + curLoading = false; if (!errorLog.trim().isEmpty()) { @@ -523,6 +533,7 @@ public void writeFile(String _fileName, boolean writeToReversionMgr) grammarManager.writeXML(doc, rootElement); toDoManager.writeXML(doc, rootElement); phraseManager.writeXML(doc, rootElement); + translationManager.writeXML(doc, rootElement); // write family entries rootElement.appendChild(famManager.writeToSaveXML(doc)); @@ -609,6 +620,10 @@ public ToDoManager getToDoManager() { public PhraseManager getPhraseManager() { return phraseManager; } + + public TranslationManager getTranslationManager() { + return this.translationManager; + } /** * Returns DictCore OS handler diff --git a/src/main/java/org/darisadesigns/polyglotlina/IOHandler.java b/src/main/java/org/darisadesigns/polyglotlina/IOHandler.java index b13a1655..ee950675 100644 --- a/src/main/java/org/darisadesigns/polyglotlina/IOHandler.java +++ b/src/main/java/org/darisadesigns/polyglotlina/IOHandler.java @@ -23,6 +23,8 @@ import org.darisadesigns.polyglotlina.ManagersCollections.LogoCollection; import org.darisadesigns.polyglotlina.ManagersCollections.ImageCollection; import org.darisadesigns.polyglotlina.ManagersCollections.ReversionManager; +import org.darisadesigns.polyglotlina.ManagersCollections.TranslationManager; + import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -204,8 +206,16 @@ public void loadLogographs(LogoCollection logoCollection, * @param fileName full path of polyglot archive * @throws IOException on read error */ - public void loadReversionStates(ReversionManager reversionManager, - String fileName) throws IOException; + public void loadReversionStates(ReversionManager reversionManager, String fileName) throws IOException; + + /** + * Loads SQLite DB file from a PolyGlot file. + * + * @param translationManager Translation manager to retrieve settings of DB file. + * @param filePath full path of polyglot archive + * @throws IOException on read error + */ + public void loadTranslationDB(TranslationManager translationManager, String filePath) throws IOException; /** * Exports font in PGD to external file diff --git a/src/main/java/org/darisadesigns/polyglotlina/ManagersCollections/TranslationManager.java b/src/main/java/org/darisadesigns/polyglotlina/ManagersCollections/TranslationManager.java new file mode 100644 index 00000000..1791eb75 --- /dev/null +++ b/src/main/java/org/darisadesigns/polyglotlina/ManagersCollections/TranslationManager.java @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2023, Draque Thompson, draquemail@gmail.com + * All rights reserved. + * + * Licensed under: MIT License + * See LICENSE.TXT included with this code to read the full license agreement. + + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package org.darisadesigns.polyglotlina.ManagersCollections; + +import java.io.File; +import java.io.IOException; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.UUID; + +import org.darisadesigns.polyglotlina.DictCore; +import org.darisadesigns.polyglotlina.Nodes.PhraseNode; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * + * @author pe1uca + */ +public class TranslationManager implements AutoCloseable { + + public enum SourceLang { + CONLANG, + LOCALLANG + } + + public enum ResultColumn { + HIGHLIGHT, + CONLANG, + LOCALLANG, + ID, + PRIORITY, + RANK + } + + public static final String ZIP_FILE_NAME = "translation.db"; + + public static final String MANAGER_XID = "translations"; + public static final String TMP_FILENAME_XID = "TmpFileName"; + + private String tmpFileName = ""; + private final DictCore core; + private Connection connection; + private boolean isInitialized = false; + private boolean newFile = true; + + public TranslationManager(DictCore core) { + this.core = core; + } + + public void setTmpFileName(String fileName) { + this.tmpFileName = fileName; + } + + public String getTmpFileName() { + if (this.tmpFileName.isEmpty()) { + UUID uuid = UUID.randomUUID(); + this.tmpFileName = uuid.toString() + ".db"; + } + return this.tmpFileName; + } + + public DictCore getCore() { + return this.core; + } + + public boolean isDBOpen() { + return null != this.connection; + } + + public void setIsInitialized(boolean isInitialized) { + this.isInitialized = isInitialized; + } + + public boolean isInitialized() { + return this.isInitialized; + } + + public Connection openConnection() throws SQLException, IOException { + return this.openConnection(this.getTmpFileName()); + } + + public Connection openConnection(String dbName) throws SQLException, IOException { + if (null != this.connection) return this.connection; + + dbName = dbName.replaceFirst("(?i)\\.db$", "") + ".db"; + File dir = core.getOSHandler().getWorkingDirectory(); + String dbPath = dir.getCanonicalPath() + File.separator + dbName; + File dbFile = new File(dbPath); + // If the file doesn't exist a new file will be created when connecting + newFile = !dbFile.exists(); + String connectionString = "jdbc:sqlite:%s".formatted(dbPath); + this.connection = DriverManager.getConnection(connectionString); + if(null == this.connection) throw new IOException("Unable to connect to translation DB"); + + return this.connection; + } + + @Override + public void close() throws Exception { + if (null == this.connection) return; + + try { + this.connection.close(); + } catch (SQLException e) { + // Ignore errors and set to null + } + this.connection = null; + } + + public boolean deleteTmpFile() throws IOException { + File dir = core.getOSHandler().getWorkingDirectory(); + String dbPath = dir.getCanonicalPath() + File.separator + this.getTmpFileName(); + File tmpFile = new File(dbPath); + return tmpFile.delete(); + } + + public void initializeTmpTranslationDB(String tokenChars, String separators) { + this.initializeTranslationDB(this.getTmpFileName(), tokenChars, separators); + } + + public void initializeTranslationDB(String dbName, String tokenChars, String separators) { + try { + // Normalize database file extension + openConnection(dbName); + + if (newFile) { + createDBSchema(tokenChars, separators); + prefillTranslations(); + } + this.isInitialized = true; + } catch (IOException | SQLException e) { + e.printStackTrace(); + System.out.println(e.getMessage()); + } + } + + private void createDBSchema(String tokenChars, String separators) throws SQLException { + try { + String fts5Create = "CREATE VIRTUAL TABLE translations USING fts5(conlang, locallang, id UNINDEXED, priority UNINDEXED, tokenize = \"unicode61 remove_diacritics 0 tokenchars '%s' separators '%s'\")"; + this.connection.createStatement().execute(fts5Create.formatted(tokenChars, separators)); + Statement statement = this.connection.createStatement(); + // If the table was just created we recreate an SQLite intermediate table to include some constraints + statement.addBatch("DROP TABLE translations_content;"); + statement.addBatch(""" + CREATE TABLE translations_content ( + id INTEGER PRIMARY KEY, -- Internal ID of SQLite + c0 NOT NULL, -- conlang + c1 NOT NULL, -- locallang + c2 UNIQUE, -- Polyglot phrase ID + c3 DEFAULT (0), -- Priority to prefer one result over other + UNIQUE(c0, c1) + ); + """); + statement.executeBatch(); + } catch (SQLException e) { + // We ignore the error if the table already existed. We throw other exceptions + if(!e.getMessage().contains("table translations already exists")) { + throw e; + } + } + } + + /** + * Retrieves all the data from the phrasebook and inserts in the DB. + * This is intended to be ran in an empty DB. + * + * @throws SQLException + */ + private void prefillTranslations() throws SQLException { + try { + // Manually control commit and rollback of the transaction + this.connection.setAutoCommit(false); + + List phrases = this.core.getPhraseManager().getAllValues(); + String sql = "INSERT INTO translations(conlang, locallang, id, priority) VALUES(?, ?, ?, 0)"; + for (PhraseNode phraseNode : phrases) { + PreparedStatement statement = this.connection.prepareStatement(sql); + + statement.setString(1, phraseNode.getConPhrase()); + statement.setString(2, phraseNode.getLocalPhrase()); + statement.setInt(3, phraseNode.getId()); + + statement.executeUpdate(); + } + + this.connection.commit(); + } catch(SQLException e) { + this.connection.rollback(); + throw e; + } finally { + // Revert to default behavior to commit each statement + this.connection.setAutoCommit(true); + } + } + + /** + * Adds a dedicated translation (not registered in phrasebook). + * Helps with small translations that user might not want to save as a full phrase. + * + * @param conlang Phrase in the conlang + * @param localLang Phrase in the local language for translation + * @throws SQLException + */ + public void addTranslation(String conlang, String localLang) throws SQLException { + this.addTranslation(-1, conlang, localLang); + } + + /** + * Adds a translation from a node in the phrasebook. + * + * @param phrase Phrasebook entry from which to extract the data + * @throws SQLException + */ + public void addTranslation(PhraseNode phrase) throws SQLException { + this.addTranslation(phrase.getId(), phrase.getConPhrase(), phrase.getLocalPhrase()); + } + + /** + * Adds a unique translation to the DB. + * The unique constraint comes from the ID in the phrasebook. + * An id of `-1` is treated as null by the DB which allows to have multiple rows with that id. + * + * @param id ID to link this translation in the phrasebook (-1 if the translation isn't present in the phrasebook) + * @param conlang Phrase in the conlang + * @param localLang Phrase in the local language for translation + * @throws SQLException + */ + private void addTranslation(int id, String conlang, String localLang) throws SQLException { + if (null == this.connection) throw new SQLException("Connection hasn't been opened"); + + String sql = "INSERT INTO translations(conlang, locallang, id, priority) VALUES(?, ?, ?, 0)"; + PreparedStatement statement = this.connection.prepareStatement(sql); + statement.setString(1, conlang); + statement.setString(2, localLang); + if (-1 == id) { + statement.setNull(3, java.sql.Types.INTEGER); + } + else { + statement.setInt(3, id); + } + + statement.executeUpdate(); + } + + public List> searchTranslationsFor(SourceLang source, String phrase) throws SQLException { + if (null == this.connection) throw new SQLException("Connection hasn't been opened"); + if (phrase.isBlank()) return new ArrayList<>(); + + int columnIdx = 0; + String column = ""; + switch (source) { + case CONLANG: + columnIdx = 0; + column = "conlang"; + break; + case LOCALLANG: + columnIdx = 1; + column = "locallang"; + break; + } + + // Try to sanitize string for the query + phrase = phrase.replaceAll("[-'\"\\?!\\.\\(\\)\\[\\]\\\\]", " "); + + String sql = """ + SELECT highlight(translations, %d, '', '') as conlang_highlight, conlang, locallang, id, priority, rank + FROM translations + WHERE %s MATCH ? order by rank + """.formatted(columnIdx, column); + // find matches with any of the words in the phrase + // OR the exact phrase + String whereMatch = "(%s) OR \"%s\"".formatted(phrase.replaceAll(" ", " OR "), phrase); + PreparedStatement statement = this.connection.prepareStatement(sql); + statement.setString(1, whereMatch); + + ResultSet res = statement.executeQuery(); + + ArrayList> result = new ArrayList<>(); + + while(res.next()) { + EnumMap row = new EnumMap<>(ResultColumn.class); + + row.put(ResultColumn.HIGHLIGHT, res.getString("conlang_highlight")); + row.put(ResultColumn.CONLANG, res.getString("conlang")); + row.put(ResultColumn.LOCALLANG, res.getString("locallang")); + row.put(ResultColumn.ID, res.getString("id")); + row.put(ResultColumn.PRIORITY, res.getString("priority")); + row.put(ResultColumn.RANK, res.getString("rank")); + + result.add(row); + } + return result; + } + + /** + * Writes manager information to XML document + * + * @param doc Document to write to + * @param rootElement root element of document + */ + public void writeXML(Document doc, Element rootElement) { + Element translationManager = doc.createElement(TranslationManager.MANAGER_XID); + + Element currentElement = doc.createElement(TranslationManager.TMP_FILENAME_XID); + currentElement.appendChild(doc.createTextNode(this.getTmpFileName())); + translationManager.appendChild(currentElement); + + rootElement.appendChild(translationManager); + } +} diff --git a/src/main/java/org/darisadesigns/polyglotlina/Screens/ScrMainMenu.form b/src/main/java/org/darisadesigns/polyglotlina/Screens/ScrMainMenu.form index 2d30dd70..63e58487 100644 --- a/src/main/java/org/darisadesigns/polyglotlina/Screens/ScrMainMenu.form +++ b/src/main/java/org/darisadesigns/polyglotlina/Screens/ScrMainMenu.form @@ -335,7 +335,7 @@ - + @@ -430,6 +430,7 @@ + @@ -458,7 +459,9 @@ - + + + @@ -587,6 +590,21 @@ + + + + + + + + + + + + + + + diff --git a/src/main/java/org/darisadesigns/polyglotlina/Screens/ScrMainMenu.java b/src/main/java/org/darisadesigns/polyglotlina/Screens/ScrMainMenu.java index ce63d258..cbc19602 100644 --- a/src/main/java/org/darisadesigns/polyglotlina/Screens/ScrMainMenu.java +++ b/src/main/java/org/darisadesigns/polyglotlina/Screens/ScrMainMenu.java @@ -119,6 +119,7 @@ public boolean isMenuReady() { private final List childWindows = new ArrayList<>(); private Thread longRunningSetup; private boolean menuReady = false; + private boolean isLoading = false; /** * Creates new form ScrMainMenu @@ -199,6 +200,23 @@ private synchronized void longRunningSetup() { longRunningSetup.start(); } + /** + * Helper method to lock the main screen for loading operations. + * (Prevents the user from changing screens). + * + * @param b + */ + public void setLoading(boolean b) { + this.isLoading = b; + setCursor(b ? Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR) : Cursor.getDefaultCursor()); + for (var container : this.pnlSideButtons.getComponents()) { + container.setEnabled(!b); + } + for (var container : this.jMenuBar1.getComponents()) { + container.setEnabled(!b); + } + } + public Thread getSetupThread() { return longRunningSetup; } @@ -1545,6 +1563,7 @@ private void initComponents() { btnQuiz = new PButton(nightMode); btnPhrasebook = new PButton(nightMode); btnZompistWordGenerator = new PButton(nightMode); + btnTranslate = new PButton(nightMode); jScrollPane2 = new javax.swing.JScrollPane(); pnlMain = new javax.swing.JPanel() { @Override @@ -1722,6 +1741,15 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { } }); + btnTranslate.setText("Translate"); + btnTranslate.setToolTipText("Translate from and to your conlang"); + btnTranslate.setPreferredSize(new java.awt.Dimension(141, 29)); + btnTranslate.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnTranslateActionPerformed(evt); + } + }); + javax.swing.GroupLayout pnlSideButtonsLayout = new javax.swing.GroupLayout(pnlSideButtons); pnlSideButtons.setLayout(pnlSideButtonsLayout); pnlSideButtonsLayout.setHorizontalGroup( @@ -1738,7 +1766,8 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { .addComponent(btnPhonology, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(btnPhrasebook, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(btnProp, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(btnQuiz, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(btnQuiz, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(btnTranslate, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap()) ); pnlSideButtonsLayout.setVerticalGroup( @@ -1764,7 +1793,9 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { .addComponent(btnProp, javax.swing.GroupLayout.PREFERRED_SIZE, 28, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(btnQuiz) - .addContainerGap(127, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(btnTranslate, javax.swing.GroupLayout.PREFERRED_SIZE, 28, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(99, Short.MAX_VALUE)) ); pnlMain.setBackground(new java.awt.Color(255, 255, 255)); @@ -1873,7 +1904,7 @@ public void mouseClicked(java.awt.event.MouseEvent evt) { .addGroup(jPanel3Layout.createSequentialGroup() .addComponent(pnlSideButtons, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 507, Short.MAX_VALUE)) + .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 504, Short.MAX_VALUE)) ); jPanel3Layout.setVerticalGroup( jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -2438,6 +2469,14 @@ private void btnPhrasebookActionPerformed(java.awt.event.ActionEvent evt) {//GEN setCursor(Cursor.getDefaultCursor()); }//GEN-LAST:event_btnPhrasebookActionPerformed + private void btnTranslateActionPerformed(java.awt.event.ActionEvent evt) { + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + saveAllValues(); + ScrTranslate s = new ScrTranslate(core); + changeScreen(s, s.getWindow(), (PButton) evt.getSource()); + setCursor(Cursor.getDefaultCursor()); + } + private void mnuUnpackLanguageActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mnuUnpackLanguageActionPerformed unpackLanguage(); }//GEN-LAST:event_mnuUnpackLanguageActionPerformed @@ -2464,6 +2503,7 @@ private void btnZompistWordGeneratorActionPerformed(java.awt.event.ActionEvent e private javax.swing.JButton btnPos; private javax.swing.JButton btnProp; private javax.swing.JButton btnQuiz; + private javax.swing.JButton btnTranslate; private javax.swing.JButton btnZompistWordGenerator; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; diff --git a/src/main/java/org/darisadesigns/polyglotlina/Screens/ScrTranslate.form b/src/main/java/org/darisadesigns/polyglotlina/Screens/ScrTranslate.form new file mode 100644 index 00000000..33e7aabf --- /dev/null +++ b/src/main/java/org/darisadesigns/polyglotlina/Screens/ScrTranslate.form @@ -0,0 +1,252 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/main/java/org/darisadesigns/polyglotlina/Screens/ScrTranslate.java b/src/main/java/org/darisadesigns/polyglotlina/Screens/ScrTranslate.java new file mode 100644 index 00000000..feec4dce --- /dev/null +++ b/src/main/java/org/darisadesigns/polyglotlina/Screens/ScrTranslate.java @@ -0,0 +1,532 @@ +/* + * Copyright (c) 2023, Draque Thompson, draquemail@gmail.com + * All rights reserved. + * + * Licensed under: MIT License + * See LICENSE.TXT included with this code to read the full license agreement. + + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.darisadesigns.polyglotlina.Screens; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.GridBagConstraints; +import java.awt.GridLayout; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.io.IOException; +import java.sql.SQLException; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import javafx.application.Platform; +import javafx.embed.swing.JFXPanel; +import javafx.event.EventType; +import javafx.scene.Node; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.input.Clipboard; +import javafx.scene.input.ClipboardContent; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.StackPane; +import javafx.scene.web.HTMLEditor; +import javafx.scene.web.HTMLEditorSkin; +import javafx.scene.web.WebEngine; +import javafx.scene.web.WebView; +import javax.swing.JComponent; +import javax.swing.JLayeredPane; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import javax.swing.text.BadLocationException; +import org.darisadesigns.polyglotlina.Desktop.CustomControls.PFrame; +import org.darisadesigns.polyglotlina.Desktop.PGTUtil; +import org.darisadesigns.polyglotlina.DictCore; +import org.darisadesigns.polyglotlina.ManagersCollections.TranslationManager; +import org.darisadesigns.polyglotlina.ManagersCollections.TranslationManager.ResultColumn; +import org.darisadesigns.polyglotlina.ManagersCollections.TranslationManager.SourceLang; +import static org.darisadesigns.polyglotlina.ManagersCollections.TranslationManager.SourceLang.CONLANG; +import static org.darisadesigns.polyglotlina.ManagersCollections.TranslationManager.SourceLang.LOCALLANG; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.nodes.TextNode; +import org.jsoup.select.Elements; + +/** + * + * @author edga_ + */ +public class ScrTranslate extends PFrame { + + private HTMLEditor editorSource; + private HTMLEditor editorTranslation; + private WebView viewTranslation; + private StackPane stackPane; + private SourceLang sourceLang = SourceLang.LOCALLANG; + + /** + * Creates new form ScrTranslate + * @param _core + */ + public ScrTranslate(DictCore _core) { + super(_core); + initComponents(); + initJavaFXComponents(); + // Hack to set divider at the middle of the screen + this.addComponentListener(new ComponentListener() { + @Override + public void componentResized(ComponentEvent e) { + ScrTranslate.this.jSplitPane2.setDividerLocation(0.5); + } + @Override + public void componentMoved(ComponentEvent e) {} + @Override + public void componentShown(ComponentEvent e) {} + @Override + public void componentHidden(ComponentEvent e) {} + }); + + if (_core.getTranslationManager().isInitialized()) { + try { + this.core.getTranslationManager().openConnection(); + } + catch (IOException | SQLException ex) { + Logger.getLogger(ScrTranslate.class.getName()).log(Level.SEVERE, null, ex); + } + this.jPanel1.setVisible(false); + } + } + + /** + * Initialized the JavaFX components and includes them in the Swing containers. + */ + private void initJavaFXComponents() { + Platform.runLater(() -> { + this.editorSource = new HTMLEditor(); + this.editorTranslation = new HTMLEditor(); + this.stackPane = new StackPane(); + this.viewTranslation = new WebView(); + this.hideEditorControls(this.editorSource); + this.hideEditorControls(this.editorTranslation); + + this.includeNodeInPanel(this.editorSource, this.jPanel6); + + JFXPanel webStackPanel = this.createJFXPanel(this.stackPane); + this.jPanel7.setLayout(new GridLayout()); + this.jPanel7.add(webStackPanel, BorderLayout.CENTER); + + this.stackPane.getChildren().addAll(this.editorTranslation, this.viewTranslation); + + this.editorSource.addEventFilter( + KeyEvent.KEY_PRESSED, + e -> { + if (e.isControlDown() && e.getCode() == KeyCode.V) { + // TODO: decide if we want to modify the clipboard + // setClipboardToPlainText(); + // At the moment, prevent pasting + e.consume(); + } + else if (e.getCode() == KeyCode.ENTER) { + ScrTranslate.this.processTranslationRequest(); + e.consume(); + } + }); + + WebView webView = (WebView) this.editorSource.lookup(".web-view"); + webView.setContextMenuEnabled(false); + + setInitialContent(webView.getEngine()); + setInitialContent(this.viewTranslation.getEngine()); + // setInitialContent(this.editorTranslation, false); + + pack(); + }); + } + + /** + * Updates the clipboard to only hold plain text. + * This modifies the content of the clipboard which is not so good for UX. + */ + private void setClipboardToPlainText() { + Clipboard clipboard = Clipboard.getSystemClipboard(); + + String plainText = clipboard.getString(); + ClipboardContent content = new ClipboardContent(); + content.putString(plainText); + + clipboard.setContent(content); + } + + /** + * Hides tool bars used to control the editor. + * + * @param editor HTMLEditor to update. + */ + private void hideEditorControls(HTMLEditor editor) { + Node[] nodes = editor.lookupAll(".tool-bar").toArray(Node[]::new); + for(Node node : nodes) + { + node.setVisible(false); + node.setManaged(false); + } + editor.setVisible(true); + } + + /** + * Creates a new JFXPanel that can be added to Swing containers. + * + * @param node JavaFX node to include in the panel. + * @return + */ + private JFXPanel createJFXPanel(Parent node) { + Scene scene = new Scene(node); + JFXPanel fxPanel = new JFXPanel(); + fxPanel.setScene(scene); + + return fxPanel; + } + + /** + * Include a JavaFX node inside a Swing container. + * + * @param node JavaFX node to include. + * @param container Target Swing container. + * @return JFXPanel that was added to the container. + */ + private JFXPanel includeNodeInPanel(Parent node, Container container) { + JFXPanel fxPanel = createJFXPanel(node); + + container.setLayout(new GridLayout()); + container.add(fxPanel, BorderLayout.CENTER); + return fxPanel; + } + + /** + * Sets the initial HTML to the given web engine. + * + * @param engine Engine to set initial html. + */ + private void setInitialContent(WebEngine engine) { + // TODO: add default CSS + String html = ""; + + engine.loadContent(html); + } + + /** + * Locks or unlocks the screen for long operations. + * + * @param b true to lock, false to unlock. + */ + public void setLoading(boolean b) { + ScrMainMenu mainScreen = (ScrMainMenu)SwingUtilities.getAncestorOfClass(ScrMainMenu.class, this.jLayeredPane1); + mainScreen.setLoading(b); + this.btnInitialize.setEnabled(!b); + } + + private void initializeTranslationDB() { + this.setLoading(true); + new Thread(() -> { + this.core.getTranslationManager().initializeTmpTranslationDB("", ""); + + ScrTranslate.this.jPanel1.setVisible(false); + ScrTranslate.this.setLoading(false); + }).start(); + } + + /** + * Process value to translate, calls translation manager to retrieve results, + * and updates values in the corresponding elements. + */ + private void processTranslationRequest() { + TranslationManager manager = this.core.getTranslationManager(); + + try { + Document doc = Jsoup.parse(this.editorSource.getHtmlText()); + System.out.println(this.editorSource.getHtmlText()); + Elements paragraphs = doc.select("body p"); + String searchString = ""; + for (Element element : paragraphs) { + searchString = element.text(); + } + + final var results = manager.searchTranslationsFor(this.sourceLang, searchString); + if (results.size() <= 0) return; + + Platform.runLater(() -> { + var bestResult = results.get(0); + switch(ScrTranslate.this.sourceLang) { + case CONLANG -> ScrTranslate.this.editorTranslation.setHtmlText(bestResult.get(ResultColumn.LOCALLANG)); + case LOCALLANG -> { + String translation = bestResult.get(ResultColumn.CONLANG); + ScrTranslate.this.editorTranslation.setHtmlText(translation); + ScrTranslate.this.viewTranslation.getEngine().loadContent(translation); + } + } + String sourceTemplate = "

%s

"; + ScrTranslate.this.editorSource.setHtmlText(sourceTemplate.formatted(bestResult.get(ResultColumn.HIGHLIGHT))); + + // Change to editor when implementing "improve translation" + // stackPane.getChildren().clear(); + // stackPane.getChildren().addAll(viewTranslation, editorTranslation); + }); + } + catch (SQLException ex) { + Logger.getLogger(ScrTranslate.class.getName()).log(Level.SEVERE, null, ex); + } + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jLayeredPane1 = new javax.swing.JLayeredPane(); + jPanel1 = new javax.swing.JPanel(); + jLabel3 = new javax.swing.JLabel(); + btnInitialize = new javax.swing.JButton(); + jPanel4 = new javax.swing.JPanel(); + jSplitPane2 = new javax.swing.JSplitPane(); + jPanel2 = new javax.swing.JPanel(); + jLabel1 = new javax.swing.JLabel(); + jPanel6 = new javax.swing.JPanel(); + jPanel3 = new javax.swing.JPanel(); + jLabel2 = new javax.swing.JLabel(); + jPanel7 = new javax.swing.JPanel(); + btnTranslate = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); + + jPanel1.setLayout(new javax.swing.BoxLayout(jPanel1, javax.swing.BoxLayout.Y_AXIS)); + + jLabel3.setText("\n

The translation engine needs to be initialized

\n

This will take the content of your phrasebook and process it to provide translations in your conlang

\n

Be miindful that this will increase considerably the size of your PGD file

\n"); + jPanel1.add(jLabel3); + + btnInitialize.setText("Initialize translation engine"); + btnInitialize.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseClicked(java.awt.event.MouseEvent evt) { + btnInitializeMouseClicked(evt); + } + }); + jPanel1.add(btnInitialize); + + jSplitPane2.setDividerLocation(300); + jSplitPane2.setResizeWeight(0.5); + + jLabel1.setText("From"); + + javax.swing.GroupLayout jPanel6Layout = new javax.swing.GroupLayout(jPanel6); + jPanel6.setLayout(jPanel6Layout); + jPanel6Layout.setHorizontalGroup( + jPanel6Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 0, Short.MAX_VALUE) + ); + jPanel6Layout.setVerticalGroup( + jPanel6Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 520, Short.MAX_VALUE) + ); + + javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2); + jPanel2.setLayout(jPanel2Layout); + jPanel2Layout.setHorizontalGroup( + jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel2Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jPanel6, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(jPanel2Layout.createSequentialGroup() + .addComponent(jLabel1) + .addGap(0, 246, Short.MAX_VALUE))) + .addContainerGap()) + ); + jPanel2Layout.setVerticalGroup( + jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel2Layout.createSequentialGroup() + .addContainerGap() + .addComponent(jLabel1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jPanel6, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addContainerGap()) + ); + + jSplitPane2.setLeftComponent(jPanel2); + + jLabel2.setText("Translation"); + + javax.swing.GroupLayout jPanel7Layout = new javax.swing.GroupLayout(jPanel7); + jPanel7.setLayout(jPanel7Layout); + jPanel7Layout.setHorizontalGroup( + jPanel7Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 0, Short.MAX_VALUE) + ); + jPanel7Layout.setVerticalGroup( + jPanel7Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 520, Short.MAX_VALUE) + ); + + javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3); + jPanel3.setLayout(jPanel3Layout); + jPanel3Layout.setHorizontalGroup( + jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel3Layout.createSequentialGroup() + .addContainerGap() + .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jPanel7, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(jPanel3Layout.createSequentialGroup() + .addComponent(jLabel2) + .addGap(0, 246, Short.MAX_VALUE))) + .addContainerGap()) + ); + jPanel3Layout.setVerticalGroup( + jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel3Layout.createSequentialGroup() + .addContainerGap() + .addComponent(jLabel2) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jPanel7, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addContainerGap()) + ); + + jSplitPane2.setRightComponent(jPanel3); + + btnTranslate.setText("Translate"); + btnTranslate.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseClicked(java.awt.event.MouseEvent evt) { + btnTranslateMouseClicked(evt); + } + }); + + javax.swing.GroupLayout jPanel4Layout = new javax.swing.GroupLayout(jPanel4); + jPanel4.setLayout(jPanel4Layout); + jPanel4Layout.setHorizontalGroup( + jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel4Layout.createSequentialGroup() + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(btnTranslate) + .addContainerGap()) + .addComponent(jSplitPane2, javax.swing.GroupLayout.Alignment.TRAILING) + ); + jPanel4Layout.setVerticalGroup( + jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel4Layout.createSequentialGroup() + .addComponent(jSplitPane2) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(btnTranslate)) + ); + + jLayeredPane1.setLayer(jPanel1, javax.swing.JLayeredPane.MODAL_LAYER); + jLayeredPane1.setLayer(jPanel4, javax.swing.JLayeredPane.DEFAULT_LAYER); + + javax.swing.GroupLayout jLayeredPane1Layout = new javax.swing.GroupLayout(jLayeredPane1); + jLayeredPane1.setLayout(jLayeredPane1Layout); + jLayeredPane1Layout.setHorizontalGroup( + jLayeredPane1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jPanel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(jLayeredPane1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, 629, Short.MAX_VALUE)) + ); + jLayeredPane1Layout.setVerticalGroup( + jLayeredPane1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jPanel4, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(jLayeredPane1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, 598, Short.MAX_VALUE)) + ); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addComponent(jLayeredPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 629, Short.MAX_VALUE) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(jLayeredPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 598, Short.MAX_VALUE) + .addContainerGap()) + ); + + pack(); + }//
//GEN-END:initComponents + + private void btnTranslateMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_btnTranslateMouseClicked + this.processTranslationRequest(); + }//GEN-LAST:event_btnTranslateMouseClicked + + private void btnInitializeMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_btnInitializeMouseClicked + this.initializeTranslationDB(); + }//GEN-LAST:event_btnInitializeMouseClicked + + @Override + public boolean canClose() { + return true; + } + + @Override + public void saveAllValues() { + // Do nothing + } + + @Override + public void updateAllValues(DictCore _core) { + // Do nothing + } + + @Override + public void addBindingToComponent(JComponent c) { + throw new UnsupportedOperationException("Not supported yet."); // Generated from nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody + } + + @Override + public Component getWindow() { + return this.jLayeredPane1; + } + + @Override + public void dispose() { + try { + this.core.getTranslationManager().close(); + } + catch (Exception ex) { + Logger.getLogger(ScrTranslate.class.getName()).log(Level.SEVERE, null, ex); + } + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton btnInitialize; + private javax.swing.JButton btnTranslate; + private javax.swing.JLabel jLabel1; + private javax.swing.JLabel jLabel2; + private javax.swing.JLabel jLabel3; + private javax.swing.JLayeredPane jLayeredPane1; + private javax.swing.JPanel jPanel1; + private javax.swing.JPanel jPanel2; + private javax.swing.JPanel jPanel3; + private javax.swing.JPanel jPanel4; + private javax.swing.JPanel jPanel6; + private javax.swing.JPanel jPanel7; + private javax.swing.JSplitPane jSplitPane2; + // End of variables declaration//GEN-END:variables +}