diff --git a/.classpath b/.classpath index 45ba4c8..0750fbc 100644 --- a/.classpath +++ b/.classpath @@ -41,17 +41,7 @@ - - - - - - - - - - - + diff --git a/README.md b/README.md index 0f660e9..c71027f 100644 --- a/README.md +++ b/README.md @@ -49,9 +49,9 @@ Technical Support | Peer support at [Groups.io](https://groups.io/g/maxprograms ## Requirements - JDK 21 or newer is required for compiling and building. Get it from [Adoptium](https://adoptium.net/). -- Apache Ant 1.10.12 or newer. Get it from [https://ant.apache.org/](https://ant.apache.org/) +- Apache Ant 1.10.14 or newer. Get it from [https://ant.apache.org/](https://ant.apache.org/) - Node.js 20.16.0 LTS or newer. Get it from [https://nodejs.org/](https://nodejs.org/) -- TypeScript 5.6.2 or newer, get it from [https://www.typescriptlang.org/](https://www.typescriptlang.org/) +- TypeScript 5.6.3 or newer, get it from [https://www.typescriptlang.org/](https://www.typescriptlang.org/) ## Building diff --git a/build.xml b/build.xml index 7884797..6ed9b12 100644 --- a/build.xml +++ b/build.xml @@ -11,9 +11,7 @@ - - - + diff --git a/html/editProject.html b/html/editProject.html new file mode 100644 index 0000000..f8d91a1 --- /dev/null +++ b/html/editProject.html @@ -0,0 +1,85 @@ + + + + + Edit Project + + + + + + + + + + +
+ + + +
+ + + + + + +
+ + + + + + + + + +
+ + + +
+ + + +
+
+ + + + + + + + + +
+ + + + + +
+ + + + + +
+
+ +
+   + +
+ + + + + \ No newline at end of file diff --git a/html/messages.html b/html/messages.html deleted file mode 100644 index d88ae49..0000000 --- a/html/messages.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - -
- - - - - - -
-
- -
- - - - \ No newline at end of file diff --git a/html/sortSegments.html b/html/sortSegments.html index 4da0976..833fa18 100644 --- a/html/sortSegments.html +++ b/html/sortSegments.html @@ -11,9 +11,9 @@ - @@ -23,13 +23,13 @@
- + +
- + - + @@ -41,7 +41,7 @@
- + diff --git a/jars/openxliff.jar b/jars/openxliff.jar index d8726f1..26c7ec6 100644 Binary files a/jars/openxliff.jar and b/jars/openxliff.jar differ diff --git a/jars/slf4j-api-2.0.13.jar b/jars/slf4j-api-2.0.13.jar deleted file mode 100644 index a800cc2..0000000 Binary files a/jars/slf4j-api-2.0.13.jar and /dev/null differ diff --git a/jars/slf4j-nop-2.0.13.jar b/jars/slf4j-nop-2.0.13.jar deleted file mode 100644 index e2e1122..0000000 Binary files a/jars/slf4j-nop-2.0.13.jar and /dev/null differ diff --git a/jars/sqlite-jdbc-3.45.3.0.jar b/jars/sqlite-jdbc-3.47.0.1-20241024.015404-1.jar similarity index 60% rename from jars/sqlite-jdbc-3.45.3.0.jar rename to jars/sqlite-jdbc-3.47.0.1-20241024.015404-1.jar index 4debbd4..8c3d91e 100644 Binary files a/jars/sqlite-jdbc-3.45.3.0.jar and b/jars/sqlite-jdbc-3.47.0.1-20241024.015404-1.jar differ diff --git a/package.json b/package.json index c14ce3c..9f6d5e7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "swordfish", "productName": "Swordfish", - "version": "5.6.5", + "version": "5.7.0", "description": "Swordfish Translation Editor", "main": "js/Swordfish.js", "scripts": { @@ -20,7 +20,7 @@ "url": "https://github.com/rmraya/Swordfish.git" }, "devDependencies": { - "electron": "^32.2.0", + "electron": "^33.2.0", "typescript": "^5.6.3" }, "dependencies": { diff --git a/src/com/maxprograms/swordfish/Constants.java b/src/com/maxprograms/swordfish/Constants.java index a42b172..cd93aa5 100644 --- a/src/com/maxprograms/swordfish/Constants.java +++ b/src/com/maxprograms/swordfish/Constants.java @@ -19,8 +19,8 @@ private Constants() { } public static final String APPNAME = "Swordfish"; - public static final String VERSION = "5.6.4"; - public static final String BUILD = "20240921_0931"; + public static final String VERSION = "5.7.0"; + public static final String BUILD = "20241117_0847"; public static final String REASON = "reason"; public static final String STATUS = "status"; diff --git a/src/com/maxprograms/swordfish/ProjectsHandler.java b/src/com/maxprograms/swordfish/ProjectsHandler.java index 5349130..1684add 100644 --- a/src/com/maxprograms/swordfish/ProjectsHandler.java +++ b/src/com/maxprograms/swordfish/ProjectsHandler.java @@ -104,6 +104,8 @@ private JSONObject processRequest(String url, String request) { try { if ("/projects/create".equals(url)) { response = createProject(request); + } else if ("/projects/update".equals(url)) { + response = updateProject(request); } else if ("/projects/list".equals(url)) { response = listProjects(); } else if ("/projects/get".equals(url)) { @@ -815,6 +817,40 @@ private JSONObject getSegmentsCount(String request) { return result; } + private JSONObject updateProject(String request) { + JSONObject result = new JSONObject(); + JSONObject json = new JSONObject(request); + String projectId = json.getString("project"); + if (!projectStores.containsKey(projectId)) { + try { + Map projects = getProjects(); + Project prj = projects.get(projectId); + XliffStore store = new XliffStore(prj.getXliff(), prj.getSourceLang().getCode(), + prj.getTargetLang().getCode()); + projectStores.put(projectId, store); + } catch (SAXException | IOException | ParserConfigurationException | URISyntaxException | SQLException e) { + logger.log(Level.ERROR, Messages.getString("ProjectsHandler.3"), e); + result.put(Constants.REASON, e.getMessage()); + return result; + } + } + try { + projectStores.get(json.getString("project")).updateProject(json); + Map projects = getProjects(); + Project project = projects.get(json.getString("project")); + project.setDescription(json.getString("description")); + project.setSourceLang(LanguageUtils.getLanguage(json.getString("srcLang"))); + project.setTargetLang(LanguageUtils.getLanguage(json.getString("tgtLang"))); + project.setClient(json.getString("client")); + project.setSubject(json.getString("subject")); + saveProjectsList(projects); + } catch (IOException | SAXException | ParserConfigurationException e) { + logger.log(Level.ERROR, e); + result.put(Constants.REASON, e.getMessage()); + } + return result; + } + private JSONObject createProject(String request) { JSONObject result = new JSONObject(); JSONObject json = new JSONObject(request); @@ -1486,9 +1522,9 @@ private JSONObject generateStatistics(String request) { try { JSONObject json = new JSONObject(request); String project = json.getString("project"); + Map projects = getProjects(); if (!projectStores.containsKey(project)) { try { - Map projects = getProjects(); Project prj = projects.get(project); XliffStore store = new XliffStore(prj.getXliff(), prj.getSourceLang().getCode(), prj.getTargetLang().getCode()); @@ -1500,7 +1536,7 @@ private JSONObject generateStatistics(String request) { return result; } } - String analysis = projectStores.get(project).generateStatistics(); + String analysis = projectStores.get(project).generateStatistics(projects.get(project).getDescription()); result.put("analysis", analysis); } catch (SQLException | JSONException | SAXException | IOException | ParserConfigurationException | URISyntaxException e) { diff --git a/src/com/maxprograms/swordfish/models/Project.java b/src/com/maxprograms/swordfish/models/Project.java index eca3fec..f61964c 100644 --- a/src/com/maxprograms/swordfish/models/Project.java +++ b/src/com/maxprograms/swordfish/models/Project.java @@ -200,10 +200,18 @@ public String getClient() { return client; } + public void setClient(String client) { + this.client = client; + } + public String getSubject() { return subject; } + public void setSubject(String subject) { + this.subject = subject; + } + @Override public int compareTo(Project o) { return creationDate.compareTo(o.getCreationDate()); diff --git a/src/com/maxprograms/swordfish/xliff/XliffStore.java b/src/com/maxprograms/swordfish/xliff/XliffStore.java index 6aa0950..47888f3 100644 --- a/src/com/maxprograms/swordfish/xliff/XliffStore.java +++ b/src/com/maxprograms/swordfish/xliff/XliffStore.java @@ -12,9 +12,12 @@ package com.maxprograms.swordfish.xliff; +import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.lang.System.Logger; import java.lang.System.Logger.Level; import java.net.URISyntaxException; @@ -191,6 +194,44 @@ protected void xFunc() throws SQLException { createTables(); } + // check if chars column exists in segments table + + String sql = "PRAGMA table_info(segments);"; + boolean charsExists = false; + try (Statement st = conn.createStatement()) { + try (ResultSet rs = st.executeQuery(sql)) { + while (rs.next()) { + if ("chars".equals(rs.getString(2))) { + charsExists = true; + break; + } + } + } + } + if (!charsExists) { + sql = "ALTER TABLE segments ADD COLUMN chars INTEGER DEFAULT 0;"; + try (Statement st = conn.createStatement()) { + st.execute(sql); + } + conn.commit(); + sql = "UPDATE segments SET chars = ? WHERE sourceText = ?;"; + try (PreparedStatement prep = conn.prepareStatement(sql)) { + sql = "SELECT sourceText FROM segments WHERE type='S';"; + try (Statement st = conn.createStatement()) { + try (ResultSet rs = st.executeQuery(sql)) { + while (rs.next()) { + String sourceText = rs.getString(1); + int chars = sourceText.length() - spaces(sourceText); + prep.setInt(1, chars); + prep.setString(2, sourceText); + prep.execute(); + } + } + } + } + conn.commit(); + } + getUnitData = conn.prepareStatement("SELECT data, compressed FROM units WHERE file=? AND unitId=?"); getSource = conn.prepareStatement( "SELECT source, sourceText, state, translate FROM segments WHERE file=? AND unitId=? AND segId=?"); @@ -307,7 +348,7 @@ private void parseDocument() throws SQLException, IOException { insertFile = conn.prepareStatement("INSERT INTO files (id, name) VALUES (?,?)"); insertUnit = conn.prepareStatement("INSERT INTO units (file, unitId, data, compressed) VALUES (?,?,?,?)"); insertSegmentStmt = conn.prepareStatement( - "INSERT INTO segments (file, unitId, segId, type, state, child, translate, tags, space, source, sourceText, target, targetText, words) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)"); + "INSERT INTO segments (file, unitId, segId, type, state, child, translate, tags, space, source, sourceText, target, targetText, words, chars) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"); insertNoteStmt = conn .prepareStatement("INSERT INTO notes (file, unitId, segId, noteId, note) values (?,?,?,?,?)"); recurse(document.getRootElement()); @@ -500,9 +541,20 @@ private synchronized void insertSegment(String file, String unit, String segment insertSegmentStmt.setString(12, (target != null ? target.toString() : "")); insertSegmentStmt.setString(13, (target != null ? XliffUtils.pureText(target) : "")); insertSegmentStmt.setInt(14, type.equals("S") ? RepetitionAnalysis.wordCount(pureSource, srcLang) : 0); + insertSegmentStmt.setInt(15, type.equals("S") ? (pureSource.length() - spaces(pureSource)) : 0); insertSegmentStmt.execute(); } + private int spaces(String text) { + int count = 0; + for (int i = 0; i < text.length(); i++) { + if (Character.isWhitespace(text.charAt(i))) { + count++; + } + } + return count; + } + private void insertMatch(String file, String unit, Element match) throws SQLException { Element originalData = match.getChild("originalData"); Element source = match.getChild("source"); @@ -1964,8 +2016,7 @@ public void exportMatches(String output, String description, String client, Stri } } - public void exportTerms(String output, String description, String subject) - throws SQLException, IOException, ParserConfigurationException { + public void exportTerms(String output, String description, String subject) throws SQLException, IOException { Element descrip = null; if (!subject.isBlank()) { descrip = new Element("descrip"); @@ -2856,7 +2907,7 @@ public void acceptAll100Matches() throws SQLException, SAXException, IOException } } - public String generateStatistics() + public String generateStatistics(String projectName) throws SQLException, SAXException, IOException, ParserConfigurationException, URISyntaxException { getPreferences(); updateXliff(); @@ -2865,15 +2916,17 @@ public String generateStatistics() Map map = new HashMap<>(); Map> filesMap = new HashMap<>(); - String sql = "SELECT file, SUM(words), COUNT(*) FROM segments WHERE type='S' GROUP BY file"; + String sql = "SELECT file, SUM(words), SUM(chars), COUNT(*) FROM segments WHERE type='S' GROUP BY file"; try (ResultSet rs = stmt.executeQuery(sql)) { while (rs.next()) { String fileId = rs.getString(1); JSONObject json = new JSONObject(); int words = rs.getInt(2); - int segments = rs.getInt(3); + int chars = rs.getInt(3); + int segments = rs.getInt(4); json.put("file", fileId); json.put("words", words); + json.put("chars", chars); json.put("segments", segments); map.put(fileId, json); } @@ -2897,35 +2950,41 @@ public String generateStatistics() } } - sql = "SELECT file, SUM(words), COUNT(*) FROM segments WHERE type = 'S' AND targettext = '' GROUP BY file"; + sql = "SELECT file, SUM(words), SUM(chars), COUNT(*) FROM segments WHERE type = 'S' AND targettext = '' GROUP BY file"; try (ResultSet rs = stmt.executeQuery(sql)) { while (rs.next()) { String fileId = rs.getString(1); int untranslated = rs.getInt(2); - int untranslatedSegments = rs.getInt(3); + int untranslatedChars = rs.getInt(3); + int untranslatedSegments = rs.getInt(4); map.get(fileId).put("untranslated", untranslated); + map.get(fileId).put("untranslatedChars", untranslatedChars); map.get(fileId).put("untranslatedSegments", untranslatedSegments); } } - sql = "SELECT file, SUM(words), COUNT(*) FROM segments WHERE type = 'S' AND targettext <> '' GROUP BY file"; + sql = "SELECT file, SUM(words), SUM(chars), COUNT(*) FROM segments WHERE type = 'S' AND targettext <> '' GROUP BY file"; try (ResultSet rs = stmt.executeQuery(sql)) { while (rs.next()) { String fileId = rs.getString(1); int translated = rs.getInt(2); - int translatedSegments = rs.getInt(3); + int translatedChars = rs.getInt(3); + int translatedSegments = rs.getInt(4); map.get(fileId).put("translated", translated); + map.get(fileId).put("translatedChars", translatedChars); map.get(fileId).put("translatedSegments", translatedSegments); } } - sql = "SELECT file, SUM(words), COUNT(*) FROM segments WHERE type = 'S' AND state = 'final' GROUP BY file"; + sql = "SELECT file, SUM(words), SUM(chars), COUNT(*) FROM segments WHERE type = 'S' AND state = 'final' GROUP BY file"; try (ResultSet rs = stmt.executeQuery(sql)) { while (rs.next()) { String fileId = rs.getString(1); int confirmed = rs.getInt(2); - int confirmedSegments = rs.getInt(3); + int confirmedChars = rs.getInt(3); + int confirmedSegments = rs.getInt(4); map.get(fileId).put("confirmed", confirmed); + map.get(fileId).put("confirmedChars", confirmedChars); map.get(fileId).put("confirmedSegments", confirmedSegments); } } @@ -3020,13 +3079,15 @@ public String generateStatistics() } } - sql = "SELECT file, SUM(words), COUNT(*) FROM segments WHERE type = 'S' AND translate = 'N' GROUP BY file"; + sql = "SELECT file, SUM(words), SUM(chars), COUNT(*) FROM segments WHERE type = 'S' AND translate = 'N' GROUP BY file"; try (ResultSet rs = stmt.executeQuery(sql)) { while (rs.next()) { String fileId = rs.getString(1); - int confirmed = rs.getInt(2); - int confirmedSegments = rs.getInt(3); - map.get(fileId).put("locked", confirmed); + int locked = rs.getInt(2); + int lockedChars = rs.getInt(3); + int confirmedSegments = rs.getInt(4); + map.get(fileId).put("locked", locked); + map.get(fileId).put("lockedChars", lockedChars); map.get(fileId).put("lockedSegments", confirmedSegments); } } @@ -3044,10 +3105,18 @@ public String generateStatistics() json.put("translated", 0); map.put(key, json); } + if (!json.has("translatedChars")) { + json.put("translatedChars", 0); + map.put(key, json); + } if (!json.has("confirmed")) { json.put("confirmed", 0); map.put(key, json); } + if (!json.has("confirmedChars")) { + json.put("confirmedChars", 0); + map.put(key, json); + } if (!json.has("translatedSegments")) { json.put("translatedSegments", 0); map.put(key, json); @@ -3056,6 +3125,10 @@ public String generateStatistics() json.put("untranslatedSegments", 0); map.put(key, json); } + if (!json.has("untranslatedChars")) { + json.put("untranslatedChars", 0); + map.put(key, json); + } if (!json.has("confirmedSegments")) { json.put("confirmedSegments", 0); map.put(key, json); @@ -3064,12 +3137,33 @@ public String generateStatistics() json.put("locked", 0); map.put(key, json); } + if (!json.has("lockedChars")) { + json.put("lockedChars", 0); + map.put(key, json); + } if (!json.has("lockedSegments")) { json.put("lockedSegments", 0); map.put(key, json); } } + String css = ""; + try (InputStream is = XliffStore.class.getResourceAsStream("styles.css")) { + StringBuffer sb = new StringBuffer(); + try (InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) { + try (BufferedReader br = new BufferedReader(reader)) { + String line; + while ((line = br.readLine()) != null) { + if (!sb.isEmpty()) { + sb.append("\n"); + } + sb.append(line); + } + } + } + css = sb.toString(); + } + File log = new File(file.getAbsolutePath() + ".log.html"); try (FileOutputStream out = new FileOutputStream(log)) { @@ -3079,49 +3173,12 @@ public String generateStatistics() writeString(out, " \n"); writeString(out, " Project Statistics\n"); writeString(out, " \n"); writeString(out, "\n"); writeString(out, "\n"); - writeString(out, "

" + XMLUtils.cleanText(file.getName()) + "

\n"); + writeString(out, "

" + XMLUtils.cleanText(projectName) + "

\n"); Set files = new TreeSet<>(); files.addAll(filesMap.keySet()); @@ -3144,7 +3201,7 @@ public String generateStatistics() writeString(out, "\n"); writeString(out, "\n"); while (it.hasNext()) { @@ -3229,7 +3286,7 @@ public String generateStatistics() writeString(out, "
#" + Messages.getString("XliffStore.8") + "" + Messages.getString("XliffStore.9") - + "100%95% - 99%85% - 95%75% - 84%50% - 84%" + + "100%95% - 99%85% - 94%75% - 84%50% - 74%" + Messages.getString("XliffStore.10") + "" + Messages.getString("XliffStore.11") + "" + Messages.getString("XliffStore.12") + "
\n"); writeString(out, "\n"); @@ -3426,6 +3483,63 @@ public String generateStatistics() writeString(out, "\n"); writeString(out, "
#" + Messages.getString("XliffStore.8") + "" + Messages.getString("XliffStore.9") - + "100%95% - 99%85% - 95%75% - 84%50% - 84%" + + "100%95% - 99%85% - 94%75% - 84%50% - 74%" + "Int. Rep." + "" + Messages.getString("XliffStore.16") + "" + Messages.getString("XliffStore.17") + "" + Messages.getString("XliffStore.12") + "
\n"); + writeString(out, "

" + Messages.getString("XliffStore.46") + "

\n"); + + count = 1; + projectWords = 0; + projectTranslated = 0; + projectUntranslated = 0; + projectConfirmed = 0; + + writeString(out, "\n"); + writeString(out, + "\n"); + it = files.iterator(); + while (it.hasNext()) { + String fileName = it.next(); + Set set = filesMap.get(fileName); + Iterator st = set.iterator(); + int fileChars = 0; + int fileUntranslated = 0; + int fileTranslated = 0; + int fileConfirmed = 0; + while (st.hasNext()) { + String key = st.next(); + JSONObject json = map.get(key); + fileChars += json.getInt("chars"); + fileTranslated += json.getInt("translatedChars"); + fileUntranslated += json.getInt("untranslatedChars"); + fileConfirmed += json.getInt("confirmedChars"); + } + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, "\n"); + projectWords += fileChars; + projectTranslated += fileTranslated; + projectUntranslated += fileUntranslated; + projectConfirmed += fileConfirmed; + } + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, "\n"); + writeString(out, "
#" + Messages.getString("XliffStore.8") + "" + + Messages.getString("XliffStore.25") + "" + Messages.getString("XliffStore.26") + + "" + Messages.getString("XliffStore.27") + "" + + Messages.getString("XliffStore.28") + "" + Messages.getString("XliffStore.12") + + "
" + count++ + "" + XMLUtils.cleanText(fileName) + "" + fileUntranslated + "" + fileTranslated + "" + (fileChars - fileConfirmed) + "" + fileConfirmed + "" + fileChars + "
 Total" + projectUntranslated + "" + projectTranslated + "" + (projectWords - projectConfirmed) + "" + projectConfirmed + "" + projectWords + "
\n"); + SvgStats svgStats = new SvgStats(); svgStats.analyse(xliffFile, catalog); @@ -4627,4 +4741,15 @@ public void setMTMatches(JSONObject json) insertMatch(file, unit, segment, origin, Constants.MT, 0, source, target, new JSONObject()); } } + + public void updateProject(JSONObject json) throws SAXException, IOException, ParserConfigurationException { + if (!srcLang.equals(json.getString("srcLang")) || !tgtLang.equals(json.getString("tgtLang"))) { + srcLang = json.getString("srcLang"); + tgtLang = json.getString("tgtLang"); + document = builder.build(xliffFile); + document.getRootElement().setAttribute("srcLang", srcLang); + document.getRootElement().setAttribute("trgLang", tgtLang); + saveXliff(); + } + } } \ No newline at end of file diff --git a/src/com/maxprograms/swordfish/xliff/styles.css b/src/com/maxprograms/swordfish/xliff/styles.css new file mode 100644 index 0000000..be93123 --- /dev/null +++ b/src/com/maxprograms/swordfish/xliff/styles.css @@ -0,0 +1,43 @@ + +* { + font-family: Arial,Helvetica,sans-serif; +} +body { + padding: 20px; +} +h1 { + font-size: 1.5em; + font-weight: lighter; +} +table { + width:100%; + border: 1px solid #aaaaaa; + border-collapse: collapse; + border-radius: 4px; +} +td { + border: 1px solid #aaaaaa; + text-align:right; + padding:4px +} +th { + font-weight: lighter; + background:#2066A3; + border: 1px solid #eeeeee; + color:white; + text-align:center; + padding:4px +} +.left { + text-align:left; +} +.total { + font-weight: bolder; + background: #efefef; +} +.center { + text-align:center; +} +.right { + text-align:right; +} diff --git a/src/com/maxprograms/swordfish/xliff/xliff.properties b/src/com/maxprograms/swordfish/xliff/xliff.properties index 10770b1..b61c69d 100644 --- a/src/com/maxprograms/swordfish/xliff/xliff.properties +++ b/src/com/maxprograms/swordfish/xliff/xliff.properties @@ -32,6 +32,7 @@ XliffStore.42=Extra Tags XliffStore.43=Different Tag XliffStore.44=Tags in wrong order XliffStore.45=Can't split segment in locked text section. +XliffStore.46=Characters XliffStore.5=Error assembling matches: {0}\n{1} XliffStore.6=TM & Repetition Analysis XliffStore.7=Segments diff --git a/src/module-info.java b/src/module-info.java index e35b89d..cae5277 100644 --- a/src/module-info.java +++ b/src/module-info.java @@ -31,5 +31,4 @@ requires transitive json; requires java.logging; requires org.xerial.sqlitejdbc; - requires org.slf4j.nop; } \ No newline at end of file diff --git a/swordfish.pdf b/swordfish.pdf index ffc5ead..f30ab2d 100644 Binary files a/swordfish.pdf and b/swordfish.pdf differ diff --git a/ts/Main.ts b/ts/Main.ts index e915810..e1d74d6 100644 --- a/ts/Main.ts +++ b/ts/Main.ts @@ -91,6 +91,9 @@ class Main { Main.electron.ipcRenderer.on('export-xliff-review', () => { this.projectsView.exportXLIFF(); }); + Main.electron.ipcRenderer.on('edit-project', () => { + this.projectsView.editProject(); + }); Main.electron.ipcRenderer.on('remove-projects', () => { this.projectsView.removeProjects(); }); @@ -316,8 +319,8 @@ class Main { Main.electron.ipcRenderer.on('set-project-glossaries', (event: Electron.IpcRendererEvent, arg: any) => { this.setProjectGlossaries(arg); }); - Main.electron.ipcRenderer.on('reload-page', (event: Electron.IpcRendererEvent, arg: any) => { - this.reloadPage(arg); + Main.electron.ipcRenderer.on('reload-page', (event: Electron.IpcRendererEvent, projectId: string) => { + this.reloadPage(projectId); }); Main.electron.ipcRenderer.on('request-statistics', () => { this.requestStatistics(); @@ -422,11 +425,10 @@ class Main { hasTags(html: string): boolean { let container: HTMLDivElement = document.createElement('div'); container.innerHTML = html; - let tags: NodeListOf = container.querySelectorAll('img'); - if (tags.length > 0) { - for (let i = 0; i < tags.length; i++) { - let img: HTMLImageElement = tags[i] as HTMLImageElement; - if (img.getAttribute('data-ref') && img.getAttribute('src').endsWith('.svg')) { + let images: NodeListOf = container.querySelectorAll('img'); + if (images.length > 0) { + for (let tag of images) { + if (tag.getAttribute('data-ref') && tag.getAttribute('src').endsWith('.svg')) { return true; } } @@ -812,10 +814,9 @@ class Main { } } - reloadPage(arg: any): void { - let project: string = arg.project; - if (Main.translationViews.has(project)) { - Main.translationViews.get(project).getSegments(); + reloadPage(projectId: string): void { + if (Main.translationViews.has(projectId)) { + Main.translationViews.get(projectId).getSegments(); } } @@ -936,23 +937,23 @@ class Main { } unlockSegments(): void { - let selected = Main.tabHolder.getSelected(); - if (Main.translationViews.has(selected)) { - Main.electron.ipcRenderer.send('unlock-all', { project: selected }); + let projectId: string = Main.tabHolder.getSelected(); + if (Main.translationViews.has(projectId)) { + Main.electron.ipcRenderer.send('unlock-all', projectId); } } tagsAnalysis(): void { - let selected = Main.tabHolder.getSelected(); - if (Main.translationViews.has(selected)) { - Main.electron.ipcRenderer.send('analyze-tags', { project: selected }); + let projectId: string = Main.tabHolder.getSelected(); + if (Main.translationViews.has(projectId)) { + Main.electron.ipcRenderer.send('analyze-tags', projectId); } } spacesAnalysis(): void { - let selected = Main.tabHolder.getSelected(); - if (Main.translationViews.has(selected)) { - Main.electron.ipcRenderer.send('analyze-spaces', { project: selected }); + let projectId: string = Main.tabHolder.getSelected(); + if (Main.translationViews.has(projectId)) { + Main.electron.ipcRenderer.send('analyze-spaces', projectId); } } diff --git a/ts/Swordfish.ts b/ts/Swordfish.ts index 0afd1bf..25c1f58 100644 --- a/ts/Swordfish.ts +++ b/ts/Swordfish.ts @@ -29,11 +29,11 @@ export class Swordfish { static importTmxWindow: BrowserWindow; static importXliffWindow: BrowserWindow; static addProjectWindow: BrowserWindow; + static editProjectWindow: BrowserWindow; static addFileWindow: BrowserWindow; static defaultLangsWindow: BrowserWindow; static spellingLangsWindow: BrowserWindow; static filterSegmentsWindow: BrowserWindow; - static messagesWindow: BrowserWindow; static tagsWindow: BrowserWindow; static replaceTextWindow: BrowserWindow; static addGlossaryWindow: BrowserWindow; @@ -136,6 +136,7 @@ export class Swordfish { static typeParam: string; static xmlFilter: string; static filterElement: any; + static editedProject: Project; static activeProject: string; static htmlContent: string; @@ -243,7 +244,10 @@ export class Swordfish { Swordfish.addFile(); }); ipcMain.on('show-add-project', () => { - Swordfish.addProject(); + Swordfish.showAddProject(); + }); + ipcMain.on('show-edit-project', (event: IpcMainEvent, project: Project) => { + Swordfish.showEditProject(project); }); ipcMain.on('export-translations', (event: IpcMainEvent, arg: any) => { Swordfish.exportProjectTranslations(arg); @@ -290,6 +294,9 @@ export class Swordfish { ipcMain.on('close-addProject', () => { Swordfish.destroyWindow(Swordfish.addProjectWindow); }); + ipcMain.on('close-editProject', () => { + Swordfish.destroyWindow(Swordfish.editProjectWindow); + }); ipcMain.on('close-addFile', () => { Swordfish.destroyWindow(Swordfish.addFileWindow); }); @@ -336,6 +343,12 @@ export class Swordfish { ipcMain.on('get-source-files', (event: IpcMainEvent) => { Swordfish.getSelectedFiles(event); }); + ipcMain.on('get-project-data', (event: IpcMainEvent) => { + event.sender.send('project-data', Swordfish.editedProject); + }); + ipcMain.on('update-project', (event: IpcMainEvent, arg: any) => { + Swordfish.updateProject(arg); + }); ipcMain.on('create-project', (event: IpcMainEvent, arg: any) => { Swordfish.createProject(arg); }); @@ -630,9 +643,6 @@ export class Swordfish { ipcMain.on('filter-options', (event: IpcMainEvent, arg: any) => { Swordfish.filterOptions(arg); }); - ipcMain.on('close-messages', () => { - Swordfish.destroyWindow(Swordfish.messagesWindow); - }); ipcMain.on('export-xliff-review', (event: IpcMainEvent, arg: any) => { Swordfish.exportXLIFF(arg); }); @@ -717,17 +727,17 @@ export class Swordfish { ipcMain.on('lock-duplicates', (event: IpcMainEvent, arg: any) => { Swordfish.lockDuplicates(arg); }); - ipcMain.on('unlock-all', (event: IpcMainEvent, arg: any) => { - Swordfish.unlockAll(arg); + ipcMain.on('unlock-all', (event: IpcMainEvent, projectId: string) => { + Swordfish.unlockAll(projectId); }); ipcMain.on('get-zoom', () => { Swordfish.mainWindow.webContents.send('set-zoom', { zoom: Swordfish.currentPreferences.zoomFactor }); }); - ipcMain.on('analyze-spaces', (event: IpcMainEvent, arg: any) => { - Swordfish.analyzeSpaces(arg); + ipcMain.on('analyze-spaces', (event: IpcMainEvent, projectId: string) => { + Swordfish.analyzeSpaces(projectId); }); - ipcMain.on('analyze-tags', (event: IpcMainEvent, arg: any) => { - Swordfish.analyzeTags(arg); + ipcMain.on('analyze-tags', (event: IpcMainEvent, projectId: string) => { + Swordfish.analyzeTags(projectId); }); ipcMain.on('export-project-html', (event: IpcMainEvent, arg: any) => { Swordfish.exportHTML(arg); @@ -855,12 +865,10 @@ export class Swordfish { } // end constructor static createWindow(): void { - if (Swordfish.currentDefaults === undefined) { let size: Size = screen.getPrimaryDisplay().workAreaSize; - Swordfish.currentDefaults = { width: Math.round(size.width * 0.9), height: Math.round(size.height * 0.9), x: 0, y: 0 }; + Swordfish.currentDefaults = { width: Math.round(size.width * 0.95), height: Math.round(size.height * 0.95), x: 0, y: 0 }; } - this.mainWindow = new BrowserWindow({ title: app.name, width: this.currentDefaults.width, @@ -874,7 +882,6 @@ export class Swordfish { show: false, icon: this.iconPath }); - this.mainWindow.webContents.on('context-menu', (event: Electron.Event, params: any) => { const menu = new Menu(); // Add each spelling suggestion @@ -979,7 +986,8 @@ export class Swordfish { viewMenu.append(new MenuItem({ label: 'Open Development Tools', accelerator: 'F12', click: () => { Swordfish.mainWindow.webContents.openDevTools() } })); } let projectsMenu: Menu = Menu.buildFromTemplate([ - { label: 'New Project', accelerator: 'CmdOrCtrl+N', click: () => { Swordfish.addProject(); } }, + { label: 'New Project', accelerator: 'CmdOrCtrl+N', click: () => { Swordfish.showAddProject(); } }, + { label: 'Edit Project', click: () => { Swordfish.editProject(); } }, { label: 'Translate Projects', click: () => { Swordfish.translateProjects(); } }, { label: 'Export Translations', accelerator: 'CmdOrCtrl+Alt+S', click: () => { Swordfish.mainWindow.webContents.send('export-translations'); } }, { label: 'Export Translations as TMX File', click: () => { Swordfish.mainWindow.webContents.send('export-translations-tmx'); } }, @@ -1197,6 +1205,9 @@ export class Swordfish { if ('addProject' === arg.window) { Swordfish.addProjectWindow.setContentSize(arg.width, arg.height, true); } + if ('editProject' === arg.window) { + Swordfish.editProjectWindow.setContentSize(arg.width, arg.height, true); + } if ('addFile' === arg.window) { Swordfish.addFileWindow.setContentSize(arg.width, arg.height, true); } @@ -1251,9 +1262,6 @@ export class Swordfish { if ('filterSegments' === arg.window) { Swordfish.filterSegmentsWindow.setContentSize(arg.width, arg.height, true); } - if ('messages' === arg.window) { - Swordfish.messagesWindow.setContentSize(arg.width, arg.height, true); - } if ('importXliff' === arg.window) { Swordfish.importXliffWindow.setContentSize(arg.width, arg.height, true); } @@ -1382,7 +1390,8 @@ export class Swordfish { static showSortSegments(params: any): void { this.sortSegmentsWindow = new BrowserWindow({ parent: this.mainWindow, - width: 400, + width: 420, + height: 305, minimizable: false, maximizable: false, resizable: false, @@ -1441,7 +1450,11 @@ export class Swordfish { Swordfish.mainWindow.webContents.send('close-tab'); } - static addProject(): void { + static editProject(): void { + Swordfish.mainWindow.webContents.send('edit-project'); + } + + static showAddProject(): void { this.addProjectWindow = new BrowserWindow({ parent: this.mainWindow, width: 920, @@ -1464,10 +1477,36 @@ export class Swordfish { this.addProjectWindow.on('close', () => { this.mainWindow.focus(); }); - Swordfish.setLocation(this.addProjectWindow, 'addProject.html'); } + static showEditProject(project: Project): void { + Swordfish.editedProject = project; + this.editProjectWindow = new BrowserWindow({ + parent: this.mainWindow, + width: 920, + height: 240, + minimizable: false, + maximizable: false, + resizable: false, + show: false, + icon: this.iconPath, + webPreferences: { + nodeIntegration: true, + contextIsolation: false + } + }); + this.editProjectWindow.setMenu(null); + this.editProjectWindow.loadURL('file://' + this.path.join(app.getAppPath(), 'html', 'editProject.html')); + this.editProjectWindow.once('ready-to-show', () => { + this.editProjectWindow.show(); + }); + this.editProjectWindow.on('close', () => { + this.mainWindow.focus(); + }); + Swordfish.setLocation(this.editProjectWindow, 'editProject.html'); + } + static exportOpenProject(arg: any): void { Swordfish.sendRequest('/projects/get', arg, (data: any) => { @@ -1631,7 +1670,8 @@ export class Swordfish { Swordfish.selectedFiles = value.filePaths; this.addFileWindow = new BrowserWindow({ parent: this.mainWindow, - width: 900, + width: 890, + height: 360, minimizable: false, maximizable: false, resizable: false, @@ -1670,6 +1710,22 @@ export class Swordfish { Swordfish.mainWindow.webContents.send('translate-projects'); } + static updateProject(data: any) { + Swordfish.sendRequest('/projects/update', data, + (data: any) => { + if (data.status === Swordfish.SUCCESS) { + Swordfish.mainWindow.webContents.send('request-projects', {}); + Swordfish.destroyWindow(Swordfish.editProjectWindow); + } else { + Swordfish.showMessage({ type: 'error', message: data.reason }); + } + }, + (reason: string) => { + Swordfish.showMessage({ type: 'error', message: reason }); + } + ); + } + static createProject(arg: any): void { if (arg.from === 'addProject') { Swordfish.destroyWindow(Swordfish.addProjectWindow); @@ -1882,7 +1938,8 @@ export class Swordfish { static showServerSettings(type: string): void { this.serverSettingsWindow = new BrowserWindow({ parent: this.mainWindow, - width: 450, + width: 440, + height: 240, minimizable: false, maximizable: false, resizable: false, @@ -1908,7 +1965,8 @@ export class Swordfish { static showBrowseDatabases() { this.browseDatabasesWindow = new BrowserWindow({ parent: this.mainWindow, - width: 650, + width: 635, + height: 355, minimizable: false, maximizable: false, resizable: false, @@ -1975,7 +2033,8 @@ export class Swordfish { static showAddMemory(): void { this.addMemoryWindow = new BrowserWindow({ parent: this.mainWindow, - width: 450, + width: 435, + height: 290, minimizable: false, maximizable: false, resizable: false, @@ -2500,6 +2559,7 @@ export class Swordfish { this.defaultLangsWindow = new BrowserWindow({ parent: this.mainWindow, width: 600, + height: 190, minimizable: false, maximizable: false, resizable: false, @@ -2629,6 +2689,7 @@ export class Swordfish { this.importTmxWindow = new BrowserWindow({ parent: this.mainWindow, width: 600, + height: 290, minimizable: false, maximizable: false, resizable: false, @@ -2655,6 +2716,7 @@ export class Swordfish { this.importGlossaryWindow = new BrowserWindow({ parent: this.mainWindow, width: 600, + height: 290, minimizable: false, maximizable: false, resizable: false, @@ -3195,7 +3257,7 @@ export class Swordfish { Swordfish.mainWindow.webContents.send('end-waiting'); Swordfish.mainWindow.webContents.send('set-status', ''); clearInterval(intervalObject); - Swordfish.mainWindow.webContents.send('reload-page', { project: arg.project }); + Swordfish.mainWindow.webContents.send('reload-page', arg.project); Swordfish.showMessage({ type: 'info', message: 'Added translations to ' + Swordfish.currentStatus.translated + ' segments' }); return; } else if (Swordfish.currentStatus.progress === Swordfish.PROCESSING) { @@ -3406,29 +3468,11 @@ export class Swordfish { default: parent = Swordfish.mainWindow; } } - Swordfish.messagesWindow = new BrowserWindow({ - parent: parent, - width: 570, - height: 180, - minimizable: false, - maximizable: false, - resizable: false, - modal: true, - show: false, + dialog.showMessageBox(parent, { icon: this.iconPath, - webPreferences: { - nodeIntegration: true, - contextIsolation: false - } - }); - Swordfish.messageParam = arg; - Swordfish.messagesWindow.setMenu(null); - Swordfish.messagesWindow.loadURL('file://' + this.path.join(app.getAppPath(), 'html', 'messages.html')); - Swordfish.messagesWindow.once('ready-to-show', () => { - Swordfish.messagesWindow.show(); - }); - Swordfish.messagesWindow.on('close', () => { - parent.focus(); + type: arg.type, + message: arg.message, + buttons: ['OK'] }); } @@ -3647,7 +3691,8 @@ export class Swordfish { static showAddGlossary(): void { this.addGlossaryWindow = new BrowserWindow({ parent: this.mainWindow, - width: 450, + width: 435, + height: 290, minimizable: false, maximizable: false, resizable: false, @@ -3769,6 +3814,7 @@ export class Swordfish { this.addFileWindow = new BrowserWindow({ parent: this.mainWindow, width: 900, + height: 355, minimizable: false, maximizable: false, resizable: false, @@ -3803,7 +3849,7 @@ export class Swordfish { } }); Swordfish.selectedFiles = filesList; - Swordfish.addProject(); + Swordfish.showAddProject(); } } @@ -3851,7 +3897,7 @@ export class Swordfish { Swordfish.showMessage({ type: 'error', message: data.reason }); return; } - Swordfish.mainWindow.webContents.send('reload-page', { project: arg.project }); + Swordfish.mainWindow.webContents.send('reload-page', arg.project); Swordfish.mainWindow.webContents.send('set-statistics', { project: arg.project, statistics: data.statistics }); }, (reason: string) => { @@ -3881,7 +3927,7 @@ export class Swordfish { Swordfish.showMessage({ type: 'error', message: data.reason }); return; } - Swordfish.mainWindow.webContents.send('reload-page', { project: arg.project }); + Swordfish.mainWindow.webContents.send('reload-page', arg.project); }, (reason: string) => { Swordfish.mainWindow.webContents.send('end-waiting'); @@ -3910,7 +3956,7 @@ export class Swordfish { Swordfish.showMessage({ type: 'error', message: data.reason }); return; } - Swordfish.mainWindow.webContents.send('reload-page', { project: arg.project }); + Swordfish.mainWindow.webContents.send('reload-page', arg.project); }, (reason: string) => { Swordfish.mainWindow.webContents.send('end-waiting'); @@ -3939,7 +3985,7 @@ export class Swordfish { Swordfish.showMessage({ type: 'error', message: data.reason }); return; } - Swordfish.mainWindow.webContents.send('reload-page', { project: arg.project }); + Swordfish.mainWindow.webContents.send('reload-page', arg.project); }, (reason: string) => { Swordfish.mainWindow.webContents.send('end-waiting'); @@ -3968,7 +4014,7 @@ export class Swordfish { Swordfish.showMessage({ type: 'error', message: data.reason }); return; } - Swordfish.mainWindow.webContents.send('reload-page', { project: arg.project }); + Swordfish.mainWindow.webContents.send('reload-page', arg.project); Swordfish.mainWindow.webContents.send('set-statistics', { project: arg.project, statistics: data.statistics }); }, (reason: string) => { @@ -3998,7 +4044,7 @@ export class Swordfish { Swordfish.showMessage({ type: 'error', message: data.reason }); return; } - Swordfish.mainWindow.webContents.send('reload-page', { project: arg.project }); + Swordfish.mainWindow.webContents.send('reload-page', arg.project); Swordfish.mainWindow.webContents.send('set-statistics', { project: arg.project, statistics: data.statistics }); }, (reason: string) => { @@ -4028,7 +4074,7 @@ export class Swordfish { Swordfish.showMessage({ type: 'error', message: data.reason }); return; } - Swordfish.mainWindow.webContents.send('reload-page', { project: arg.project }); + Swordfish.mainWindow.webContents.send('reload-page', arg.project); Swordfish.mainWindow.webContents.send('set-statistics', { project: arg.project, statistics: data.statistics }); }, (reason: string) => { @@ -4066,7 +4112,7 @@ export class Swordfish { Swordfish.mainWindow.webContents.send('end-waiting'); Swordfish.mainWindow.webContents.send('set-status', ''); clearInterval(intervalObject); - Swordfish.mainWindow.webContents.send('reload-page', { project: arg.project }); + Swordfish.mainWindow.webContents.send('reload-page', arg.project); Swordfish.mainWindow.webContents.send('set-statistics', { project: arg.project, statistics: data.statistics }); return; } else if (Swordfish.currentStatus.progress === Swordfish.PROCESSING) { @@ -4115,7 +4161,7 @@ export class Swordfish { Swordfish.showMessage({ type: 'error', message: data.reason }); return; } - Swordfish.mainWindow.webContents.send('reload-page', { project: arg.project }); + Swordfish.mainWindow.webContents.send('reload-page', arg.project); Swordfish.mainWindow.webContents.send('set-statistics', { project: arg.project, statistics: data.statistics }); }, (reason: string) => { @@ -4185,7 +4231,8 @@ export class Swordfish { static showGoToWindow(): void { this.goToWindow = new BrowserWindow({ parent: this.mainWindow, - width: 260, + width: 250, + height: 150, minimizable: false, maximizable: false, resizable: false, @@ -4217,6 +4264,7 @@ export class Swordfish { this.replaceTextWindow = new BrowserWindow({ parent: this.mainWindow, width: 450, + height: 265, minimizable: false, maximizable: false, resizable: false, @@ -4251,7 +4299,7 @@ export class Swordfish { return; } Swordfish.destroyWindow(Swordfish.replaceTextWindow); - Swordfish.mainWindow.webContents.send('reload-page', { project: arg.project }); + Swordfish.mainWindow.webContents.send('reload-page', arg.project); Swordfish.mainWindow.webContents.send('set-statistics', { project: arg.project, statistics: data.statistics }); }, (reason: string) => { @@ -4286,7 +4334,7 @@ export class Swordfish { Swordfish.mainWindow.webContents.send('end-waiting'); Swordfish.mainWindow.webContents.send('set-status', ''); clearInterval(intervalObject); - Swordfish.mainWindow.webContents.send('reload-page', { project: arg.project }); + Swordfish.mainWindow.webContents.send('reload-page', arg.project); return; } else if (Swordfish.currentStatus.progress === Swordfish.PROCESSING) { // it's OK, keep waiting @@ -4353,7 +4401,7 @@ export class Swordfish { unlinkSync(exportedFile); Swordfish.mainWindow.webContents.send('end-waiting'); Swordfish.mainWindow.webContents.send('set-status', ''); - Swordfish.mainWindow.webContents.send('reload-page', { project: arg.project }); + Swordfish.mainWindow.webContents.send('reload-page', arg.project); } catch (e) { Swordfish.mainWindow.webContents.send('end-waiting'); Swordfish.mainWindow.webContents.send('set-status', ''); @@ -4430,7 +4478,7 @@ export class Swordfish { Swordfish.showMessage({ type: 'error', message: data.reason }); return; } - Swordfish.mainWindow.webContents.send('reload-page', { project: arg.project }); + Swordfish.mainWindow.webContents.send('reload-page', arg.project); Swordfish.mainWindow.webContents.send('set-statistics', { project: arg.project, statistics: data.statistics }); }, (reason: string) => { @@ -4583,6 +4631,7 @@ export class Swordfish { this.termSearchWindow = new BrowserWindow({ parent: this.mainWindow, width: 500, + height: 280, minimizable: false, maximizable: false, resizable: false, @@ -4733,7 +4782,7 @@ export class Swordfish { Swordfish.mainWindow.webContents.send('set-status', ''); clearInterval(intervalObject); if (Swordfish.currentStatus.segments > 0) { - Swordfish.mainWindow.webContents.send('reload-page', { project: arg.project }); + Swordfish.mainWindow.webContents.send('reload-page', arg.project); Swordfish.showMessage({ type: 'info', message: 'Added terms to ' + Swordfish.currentStatus.segments + ' segments' }); return; } @@ -4786,7 +4835,7 @@ export class Swordfish { Swordfish.showMessage({ type: 'error', message: data.reason }); return; } - Swordfish.mainWindow.webContents.send('reload-page', { project: arg.project }); + Swordfish.mainWindow.webContents.send('reload-page', arg.project); }, (reason: string) => { Swordfish.showMessage({ type: 'error', message: reason }); @@ -4794,14 +4843,14 @@ export class Swordfish { ); } - static unlockAll(arg: any) { - Swordfish.sendRequest('/projects/unlockAll', arg, + static unlockAll(projectId: string) { + Swordfish.sendRequest('/projects/unlockAll', projectId, (data: any) => { if (data.status !== Swordfish.SUCCESS) { Swordfish.showMessage({ type: 'error', message: data.reason }); return; } - Swordfish.mainWindow.webContents.send('reload-page', { project: arg.project }); + Swordfish.mainWindow.webContents.send('reload-page', projectId); }, (reason: string) => { Swordfish.showMessage({ type: 'error', message: reason }); @@ -4809,11 +4858,12 @@ export class Swordfish { ); } - static analyzeSpaces(arg: any): void { - Swordfish.activeProject = arg.project; + static analyzeSpaces(projectId: string): void { + Swordfish.activeProject = projectId; Swordfish.spaceAnalysisWindow = new BrowserWindow({ parent: this.mainWindow, width: 400, + height: 350, minimizable: false, maximizable: false, resizable: false, @@ -4835,11 +4885,12 @@ export class Swordfish { Swordfish.setLocation(Swordfish.spaceAnalysisWindow, 'spaceAnalysis.html'); } - static analyzeTags(arg: any): void { - Swordfish.activeProject = arg.project; + static analyzeTags(projectId: string): void { + Swordfish.activeProject = projectId; Swordfish.tagsAnalysisWindow = new BrowserWindow({ parent: this.mainWindow, width: 400, + height: 350, minimizable: false, maximizable: false, resizable: false, @@ -4923,6 +4974,7 @@ export class Swordfish { this.changeCaseWindow = new BrowserWindow({ parent: this.mainWindow, width: 250, + height: 350, minimizable: false, maximizable: false, resizable: false, @@ -5052,6 +5104,7 @@ export class Swordfish { Swordfish.addNoteWindow = new BrowserWindow({ parent: Swordfish.notesWindow, width: 350, + height: 180, minimizable: false, maximizable: false, resizable: true, @@ -5214,6 +5267,7 @@ export class Swordfish { Swordfish.editXmlFilterWindow = new BrowserWindow({ parent: Swordfish.settingsWindow, width: 800, + height: 405, minimizable: false, maximizable: false, resizable: true, @@ -5409,6 +5463,7 @@ export class Swordfish { Swordfish.addXmlConfigurationWindow = new BrowserWindow({ parent: Swordfish.settingsWindow, width: 450, + height: 150, minimizable: false, maximizable: false, resizable: false, diff --git a/ts/addProject.ts b/ts/addProject.ts index dabb235..3f08927 100644 --- a/ts/addProject.ts +++ b/ts/addProject.ts @@ -51,7 +51,8 @@ class AddProject { this.electron.ipcRenderer.send('get-types'); this.electron.ipcRenderer.on('set-types', (event: Electron.IpcRendererEvent, arg: any) => { this.setTypes(arg); - }); this.electron.ipcRenderer.send('get-charsets'); + }); + this.electron.ipcRenderer.send('get-charsets'); this.electron.ipcRenderer.on('set-charsets', (event: Electron.IpcRendererEvent, arg: any) => { this.setCharsets(arg); }); diff --git a/ts/editProject.ts b/ts/editProject.ts new file mode 100644 index 0000000..440fb87 --- /dev/null +++ b/ts/editProject.ts @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2007 - 2024 Maxprograms. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-v10.html + * + * Contributors: + * Maxprograms - initial API and implementation + *******************************************************************************/ + +class EditProject { + + electron = require('electron'); + projectId: string; + + constructor() { + this.electron.ipcRenderer.send('get-theme'); + this.electron.ipcRenderer.on('set-theme', (event: Electron.IpcRendererEvent, arg: any) => { + (document.getElementById('theme') as HTMLLinkElement).href = arg; + }); + this.electron.ipcRenderer.send('get-clients'); + this.electron.ipcRenderer.on('set-clients', (event: Electron.IpcRendererEvent, arg: any) => { + this.setClients(arg); + }); + this.electron.ipcRenderer.send('get-subjects'); + this.electron.ipcRenderer.on('set-subjects', (event: Electron.IpcRendererEvent, arg: any) => { + this.setSubjects(arg); + }); + this.electron.ipcRenderer.send('get-languages'); + this.electron.ipcRenderer.on('set-languages', (event: Electron.IpcRendererEvent, arg: any) => { + this.setLanguages(arg); + this.electron.ipcRenderer.send('get-project-data'); + }); + this.electron.ipcRenderer.on('project-data', (event: Electron.IpcRendererEvent, project: Project) => { + this.projectId = project.id; + (document.getElementById('nameInput') as HTMLInputElement).value = project.description; + (document.getElementById('subjectInput') as HTMLInputElement).value = project.subject; + (document.getElementById('clientInput') as HTMLInputElement).value = project.client; + (document.getElementById('srcLangSelect') as HTMLSelectElement).value = project.sourceLang; + (document.getElementById('tgtLangSelect') as HTMLSelectElement).value = project.targetLang; + (document.getElementById('nameInput') as HTMLInputElement).focus(); + }); + document.addEventListener('keydown', (event: KeyboardEvent) => { + if (event.code === 'Enter' || event.code === 'NumpadEnter') { + this.updateProject(); + } + if (event.code === 'Escape') { + this.electron.ipcRenderer.send('close-editProject'); + } + }); + document.getElementById('updateProjectButton').addEventListener('click', () => { + this.updateProject(); + }); + setTimeout(() => { + this.electron.ipcRenderer.send('set-height', { window: 'editProject', width: document.body.clientWidth, height: document.body.clientHeight }); + }, 200); + } + + updateProject(): void { + let name: string = (document.getElementById('nameInput') as HTMLInputElement).value; + if (name === '') { + this.electron.ipcRenderer.send('show-message', { type: 'warning', message: 'Enter name', parent: 'addProject' }); + return; + } + let subject: string = (document.getElementById('subjectInput') as HTMLInputElement).value; + let client: string = (document.getElementById('clientInput') as HTMLInputElement).value; + let srcLang = (document.getElementById('srcLangSelect') as HTMLSelectElement).value; + if (srcLang === 'none') { + this.electron.ipcRenderer.send('show-message', { type: 'warning', message: 'Select source language', parent: 'addProject' }); + return; + } + let tgtLang = (document.getElementById('tgtLangSelect') as HTMLSelectElement).value; + if (tgtLang === 'none') { + this.electron.ipcRenderer.send('show-message', { type: 'warning', message: 'Select target language', parent: 'addProject' }); + return; + } + + let params: any = { + project: this.projectId, + description: name, + subject: subject, + client: client, + srcLang: srcLang, + tgtLang: tgtLang + } + this.electron.ipcRenderer.send('update-project', params); + } + + setClients(clients: string[]): void { + let options: string = ''; + let length: number = clients.length; + for (let i = 0; i < length; i++) { + options = options + ''; + } + document.getElementById('clients').innerHTML = options; + } + + setSubjects(subjects: string[]): void { + let options: string = ''; + for (let subject of subjects) { + options = options + ''; + } + document.getElementById('subjects').innerHTML = options; + } + + setLanguages(arg: any): void { + let array = arg.languages; + let languageOptions = ''; + for (let lang of array) { + languageOptions = languageOptions + ''; + } + document.getElementById('srcLangSelect').innerHTML = languageOptions; + document.getElementById('tgtLangSelect').innerHTML = languageOptions; + } +} diff --git a/ts/messages.ts b/ts/messages.ts deleted file mode 100644 index 15ec693..0000000 --- a/ts/messages.ts +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2007 - 2024 Maxprograms. - * - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 1.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/org/documents/epl-v10.html - * - * Contributors: - * Maxprograms - initial API and implementation - *******************************************************************************/ - -class Messages { - - electron = require('electron'); - - static SVG_ALERT: string = ''; - static SVG_INFO: string = ''; - static SVG_ERROR: string = ''; - - constructor() { - this.electron.ipcRenderer.send('get-theme'); - this.electron.ipcRenderer.on('set-theme', (event: Electron.IpcRendererEvent, arg: any) => { - (document.getElementById('theme') as HTMLLinkElement).href = arg; - }); - this.electron.ipcRenderer.send('get-message-param'); - this.electron.ipcRenderer.on('set-message', (event: Electron.IpcRendererEvent, arg: any) => { - this.setMessage(arg); - }); - document.getElementById('closeButton').addEventListener('click', () => { - this.electron.ipcRenderer.send('close-messages'); - }); - document.addEventListener('keydown', (event: KeyboardEvent) => { - if (event.code === 'Escape' || event.code === 'Enter' || event.code === 'NumpadEnter') { - this.electron.ipcRenderer.send('close-messages'); - } - }); - } - - setMessage(arg: any): void { - if (arg.type === 'warning') { - document.getElementById('icon').innerHTML = Messages.SVG_ALERT; - document.getElementById('title').innerText = 'Warning'; - } else if (arg.type === 'error') { - document.getElementById('icon').innerHTML = Messages.SVG_ERROR; - document.getElementById('title').innerText = 'Error'; - } else { - document.getElementById('icon').innerHTML = Messages.SVG_INFO; - document.getElementById('title').innerText = 'Information'; - } - if (arg.title) { - document.getElementById('title').innerText = arg.title; - } - document.getElementById('message').innerHTML = arg.message; - setTimeout(() => { - this.electron.ipcRenderer.send('set-height', { window: 'messages', width: document.body.clientWidth, height: document.body.clientHeight }); - }, 200); - } -} diff --git a/ts/projects.ts b/ts/projects.ts index 497c349..f4cb057 100644 --- a/ts/projects.ts +++ b/ts/projects.ts @@ -34,7 +34,6 @@ class Project { } class ProjectsView { - electron = require('electron'); container: HTMLDivElement; @@ -73,6 +72,15 @@ class ProjectsView { }); this.topBar.appendChild(addProjectButton); + let editProjectButton = document.createElement('a'); + editProjectButton.innerHTML = '' + + 'Edit Project'; + editProjectButton.className = 'tooltip'; + editProjectButton.addEventListener('click', () => { + this.editProject(); + }); + this.topBar.appendChild(editProjectButton); + let translateButton = document.createElement('a'); translateButton.innerHTML = '' + 'Translate Project'; @@ -392,6 +400,22 @@ class ProjectsView { this.electron.ipcRenderer.send('show-add-project'); } + editProject() { + let selected: Map = this.getSelectedProjects(); + if (selected.size === 0) { + this.electron.ipcRenderer.send('show-message', { type: 'warning', message: 'Select project' }); + return; + } + if (selected.size > 1) { + this.electron.ipcRenderer.send('show-message', { type: 'warning', message: 'Select one project' }); + return; + } + for (let key of selected.keys()) { + let project: Project = selected.get(key); + this.electron.ipcRenderer.send('show-edit-project', project); + } + } + getSelectedProjects(): Map { let selected: Map = new Map(); let checkboxes: HTMLCollectionOf = document.getElementsByClassName('projectSelection'); @@ -566,6 +590,7 @@ class ProjectsView { } displayProjects() { + let maxChars: number = this.calcChars(); if (this.projectSortAscending) { (document.getElementById('project-' + this.projectSortFielD) as HTMLTableCellElement).classList.add('arrow-up'); } else { @@ -676,8 +701,8 @@ class ProjectsView { td = document.createElement('td'); td.classList.add('list'); - if (p.description.length > 90 && (p.description.indexOf('/') != -1 || p.description.indexOf('\\') != -1)) { - td.innerText = p.description.substring(0, 30) + ' ... ' + p.description.substring(p.description.length - 50); + if (p.description.length > maxChars && (p.description.indexOf('/') != -1 || p.description.indexOf('\\') != -1)) { + td.innerText = p.description.substring(0, 30) + ' ... ' + p.description.substring(p.description.length - maxChars); td.title = p.description; } else { td.innerText = p.description; @@ -731,6 +756,26 @@ class ProjectsView { } } + calcChars(): number { + let sectionWidth = this.tableContainer.clientWidth * 0.35; + if (sectionWidth > 350) { + sectionWidth = 350; + } + let canvas = document.createElement('canvas'); + canvas.style.font = this.tbody.style.font; + let context = canvas.getContext('2d'); + if (!context) { + console.log('no context'); + return 50; + } + let longString: string = ''; + for (let i = 0; i < 100; i++) { + longString += 'M'; + } + let width = context.measureText(longString).width; + return Math.floor(sectionWidth / width * 100) - 5; + } + dblclicked(tr: HTMLTableRowElement, checkbox: HTMLInputElement): void { this.clearSelection(); checkbox.checked = true; diff --git a/ts/translation.ts b/ts/translation.ts index b59e2a6..e3324d6 100644 --- a/ts/translation.ts +++ b/ts/translation.ts @@ -496,7 +496,7 @@ class TranslationView { tagsAnalysisButton.className = 'tooltip'; tagsAnalysisButton.style.marginLeft = '20px'; tagsAnalysisButton.addEventListener('click', () => { - Main.electron.ipcRenderer.send('analyze-tags', { project: this.projectId }); + Main.electron.ipcRenderer.send('analyze-tags', this.projectId); }); this.topBar.appendChild(tagsAnalysisButton); @@ -505,7 +505,7 @@ class TranslationView { 'Check Initial/Trailing Spaces'; spaceAnalysisButton.className = 'tooltip'; spaceAnalysisButton.addEventListener('click', () => { - Main.electron.ipcRenderer.send('analyze-spaces', { project: this.projectId }); + Main.electron.ipcRenderer.send('analyze-spaces', this.projectId); }); this.topBar.appendChild(spaceAnalysisButton); @@ -1812,12 +1812,10 @@ class TranslationView { removeTag(tag: string): void { let target: HTMLTableCellElement = this.currentRow.getElementsByClassName('target')[0] as HTMLTableCellElement; - let children: HTMLCollectionOf = target.getElementsByTagName('img'); - let length: number = children.length; - for (let i = 0; i < length; i++) { - let child: HTMLElement = children[i]; - if (tag === child.getAttribute('data-id')) { - target.removeChild(child); + let images: HTMLCollectionOf = target.getElementsByTagName('img'); + for (let img of images) { + if (tag === img.getAttribute('data-id')) { + target.removeChild(img); return; } }