Initial commit
This commit is contained in:
parent
0e16479a03
commit
f434643ca1
@ -1,6 +1,6 @@
|
|||||||
# intellij-i18n
|
# easy-i18n
|
||||||
|
|
||||||

|

|
||||||
[](https://plugins.jetbrains.com/plugin/PLUGIN_ID)
|
[](https://plugins.jetbrains.com/plugin/PLUGIN_ID)
|
||||||
[](https://plugins.jetbrains.com/plugin/PLUGIN_ID)
|
[](https://plugins.jetbrains.com/plugin/PLUGIN_ID)
|
||||||
|
|
||||||
@ -25,12 +25,12 @@ To keep everything working, do not remove `<!-- ... -->` sections.
|
|||||||
|
|
||||||
- Using IDE built-in plugin system:
|
- Using IDE built-in plugin system:
|
||||||
|
|
||||||
<kbd>Settings/Preferences</kbd> > <kbd>Plugins</kbd> > <kbd>Marketplace</kbd> > <kbd>Search for "intellij-i18n"</kbd> >
|
<kbd>Settings/Preferences</kbd> > <kbd>Plugins</kbd> > <kbd>Marketplace</kbd> > <kbd>Search for "easy-i18n"</kbd> >
|
||||||
<kbd>Install Plugin</kbd>
|
<kbd>Install Plugin</kbd>
|
||||||
|
|
||||||
- Manually:
|
- Manually:
|
||||||
|
|
||||||
Download the [latest release](https://github.com/marhali/intellij-i18n/releases/latest) and install it manually using
|
Download the [latest release](https://github.com/marhali/easy-i18n/releases/latest) and install it manually using
|
||||||
<kbd>Settings/Preferences</kbd> > <kbd>Plugins</kbd> > <kbd>⚙️</kbd> > <kbd>Install plugin from disk...</kbd>
|
<kbd>Settings/Preferences</kbd> > <kbd>Plugins</kbd> > <kbd>⚙️</kbd> > <kbd>Install plugin from disk...</kbd>
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ pluginUntilBuild = 203.*
|
|||||||
pluginVerifierIdeVersions = 2020.2.4, 2020.3.2, 2021.1
|
pluginVerifierIdeVersions = 2020.2.4, 2020.3.2, 2021.1
|
||||||
|
|
||||||
platformType = IC
|
platformType = IC
|
||||||
platformVersion = 2020.2.4
|
platformVersion = 2020.3.2
|
||||||
platformDownloadSources = true
|
platformDownloadSources = true
|
||||||
# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
|
# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
|
||||||
# Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22
|
# Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22
|
||||||
|
39
src/main/java/de/marhali/easyi18n/SettingsService.java
Normal file
39
src/main/java/de/marhali/easyi18n/SettingsService.java
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package de.marhali.easyi18n;
|
||||||
|
|
||||||
|
import com.intellij.openapi.components.PersistentStateComponent;
|
||||||
|
import com.intellij.openapi.components.ServiceManager;
|
||||||
|
import com.intellij.openapi.components.State;
|
||||||
|
import com.intellij.openapi.project.Project;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.data.SettingsState;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persistent settings storage at project level.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
@State(name = "EasyI18nSettings")
|
||||||
|
public class SettingsService implements PersistentStateComponent<SettingsState> {
|
||||||
|
|
||||||
|
public static SettingsService getInstance(Project project) {
|
||||||
|
ServiceManager.getService(project, SettingsService.class).initializeComponent();
|
||||||
|
return ServiceManager.getService(project, SettingsService.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SettingsState state;
|
||||||
|
|
||||||
|
public SettingsService() {
|
||||||
|
this.state = new SettingsState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull SettingsState getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadState(@NotNull SettingsState state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
package de.marhali.easyi18n;
|
||||||
|
|
||||||
|
import com.intellij.openapi.actionSystem.AnAction;
|
||||||
|
import com.intellij.openapi.project.Project;
|
||||||
|
import com.intellij.openapi.wm.ToolWindow;
|
||||||
|
import com.intellij.openapi.wm.ToolWindowFactory;
|
||||||
|
import com.intellij.ui.content.Content;
|
||||||
|
import com.intellij.ui.content.ContentFactory;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.data.DataStore;
|
||||||
|
import de.marhali.easyi18n.ui.action.*;
|
||||||
|
import de.marhali.easyi18n.ui.panel.TableView;
|
||||||
|
import de.marhali.easyi18n.ui.panel.TreeView;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TranslatorToolWindowFactory implements ToolWindowFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {
|
||||||
|
ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();
|
||||||
|
|
||||||
|
// Translations tree view
|
||||||
|
TreeView treeView = new TreeView(project);
|
||||||
|
Content treeContent = contentFactory.createContent(treeView.getRootPanel(),"TreeView", false);
|
||||||
|
toolWindow.getContentManager().addContent(treeContent);
|
||||||
|
|
||||||
|
// Translations table view
|
||||||
|
TableView tableView = new TableView(project);
|
||||||
|
Content tableContent = contentFactory.createContent(tableView.getRootPanel(), "TableView", false);
|
||||||
|
toolWindow.getContentManager().addContent(tableContent);
|
||||||
|
|
||||||
|
// ToolWindow Actions (Can be used for every view)
|
||||||
|
List<AnAction> actions = new ArrayList<>();
|
||||||
|
actions.add(new AddAction());
|
||||||
|
actions.add(new ReloadAction());
|
||||||
|
actions.add(new SettingsAction());
|
||||||
|
actions.add(new SearchAction((searchString) -> DataStore.getInstance(project).searchBeyKey(searchString)));
|
||||||
|
toolWindow.setTitleActions(actions);
|
||||||
|
|
||||||
|
// Initialize Window Manager
|
||||||
|
WindowManager.getInstance().initialize(toolWindow, treeView, tableView);
|
||||||
|
|
||||||
|
// Initialize data store and load from disk
|
||||||
|
DataStore store = DataStore.getInstance(project);
|
||||||
|
store.addSynchronizer(treeView);
|
||||||
|
store.addSynchronizer(tableView);
|
||||||
|
|
||||||
|
try {
|
||||||
|
store.reloadFromDisk();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
src/main/java/de/marhali/easyi18n/WindowManager.java
Normal file
39
src/main/java/de/marhali/easyi18n/WindowManager.java
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package de.marhali.easyi18n;
|
||||||
|
|
||||||
|
import com.intellij.openapi.wm.ToolWindow;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.ui.panel.TableView;
|
||||||
|
import de.marhali.easyi18n.ui.panel.TreeView;
|
||||||
|
|
||||||
|
public class WindowManager {
|
||||||
|
|
||||||
|
private static WindowManager INSTANCE;
|
||||||
|
|
||||||
|
private ToolWindow toolWindow;
|
||||||
|
private TreeView treeView;
|
||||||
|
private TableView tableView;
|
||||||
|
|
||||||
|
public static WindowManager getInstance() {
|
||||||
|
return INSTANCE == null ? INSTANCE = new WindowManager() : INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private WindowManager() {}
|
||||||
|
|
||||||
|
public void initialize(ToolWindow toolWindow, TreeView treeView, TableView tableView) {
|
||||||
|
this.toolWindow = toolWindow;
|
||||||
|
this.treeView = treeView;
|
||||||
|
this.tableView = tableView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ToolWindow getToolWindow() {
|
||||||
|
return toolWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TreeView getTreeView() {
|
||||||
|
return treeView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TableView getTableView() {
|
||||||
|
return tableView;
|
||||||
|
}
|
||||||
|
}
|
116
src/main/java/de/marhali/easyi18n/data/DataStore.java
Normal file
116
src/main/java/de/marhali/easyi18n/data/DataStore.java
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
package de.marhali.easyi18n.data;
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.SettingsService;
|
||||||
|
import de.marhali.easyi18n.io.translator.TranslatorIO;
|
||||||
|
import de.marhali.easyi18n.model.DataSynchronizer;
|
||||||
|
import de.marhali.easyi18n.model.KeyedTranslation;
|
||||||
|
import de.marhali.easyi18n.model.TranslationDelete;
|
||||||
|
import de.marhali.easyi18n.model.TranslationUpdate;
|
||||||
|
import de.marhali.easyi18n.util.IOUtil;
|
||||||
|
import de.marhali.easyi18n.util.TranslationsUtil;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Singleton service to manage localized messages.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class DataStore {
|
||||||
|
|
||||||
|
private static DataStore INSTANCE;
|
||||||
|
|
||||||
|
private final Project project;
|
||||||
|
private final List<DataSynchronizer> synchronizer;
|
||||||
|
|
||||||
|
private Translations translations;
|
||||||
|
private String searchQuery;
|
||||||
|
|
||||||
|
public static DataStore getInstance(Project project) {
|
||||||
|
return INSTANCE == null ? INSTANCE = new DataStore(project) : INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataStore(Project project) {
|
||||||
|
this.project = project;
|
||||||
|
this.synchronizer = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSynchronizer(DataSynchronizer synchronizer) {
|
||||||
|
this.synchronizer.add(synchronizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reloadFromDisk() throws IOException {
|
||||||
|
String localesPath = SettingsService.getInstance(project).getState().getLocalesPath();
|
||||||
|
|
||||||
|
if(localesPath == null || localesPath.isEmpty()) {
|
||||||
|
translations = new Translations(new ArrayList<>(),
|
||||||
|
new LocalizedNode(LocalizedNode.ROOT_KEY, new ArrayList<>()));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
TranslatorIO io = IOUtil.determineFormat(localesPath);
|
||||||
|
translations = io.read(localesPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Propagate changes
|
||||||
|
synchronizer.forEach(synchronizer -> synchronizer.synchronize(translations, searchQuery));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveToDisk() {
|
||||||
|
String localesPath = SettingsService.getInstance(project).getState().getLocalesPath();
|
||||||
|
|
||||||
|
if(localesPath == null || localesPath.isEmpty()) { // Cannot save without valid path
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TranslatorIO io = IOUtil.determineFormat(localesPath);
|
||||||
|
io.save(translations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void searchBeyKey(String fullPath) {
|
||||||
|
// Use synchronizer to propagate search instance to all views
|
||||||
|
synchronizer.forEach(synchronizer -> synchronizer.synchronize(translations, this.searchQuery = fullPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void processUpdate(TranslationUpdate update) {
|
||||||
|
if(update.isDeletion() || update.isKeyChange()) { // Delete origin i18n key
|
||||||
|
String originKey = update.getOrigin().getKey();
|
||||||
|
List<String> sections = TranslationsUtil.getSections(originKey);
|
||||||
|
String nodeKey = sections.remove(sections.size() - 1); // Remove last node, which needs to be removed by parent
|
||||||
|
|
||||||
|
LocalizedNode node = translations.getNodes();
|
||||||
|
for(String section : sections) {
|
||||||
|
if(node == null) { // Might be possible on multi-delete
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = node.getChildren(section);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(node != null) { // Only remove if parent exists. Might be already deleted on multi-delete
|
||||||
|
node.removeChildren(nodeKey);
|
||||||
|
|
||||||
|
// Parent is empty now, we need to remove it as well (except root)
|
||||||
|
if(node.getChildren().isEmpty() && !node.getKey().equals(LocalizedNode.ROOT_KEY)) {
|
||||||
|
processUpdate(new TranslationDelete(new KeyedTranslation(
|
||||||
|
TranslationsUtil.sectionsToFullPath(sections), null)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!update.isDeletion()) { // Recreate with changed val / create
|
||||||
|
LocalizedNode node = translations.getOrCreateNode(update.getChange().getKey());
|
||||||
|
node.setValue(update.getChange().getTranslations());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Propagate changes and save them
|
||||||
|
synchronizer.forEach(synchronizer -> synchronizer.synchronize(translations, searchQuery));
|
||||||
|
saveToDisk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Translations getTranslations() {
|
||||||
|
return translations;
|
||||||
|
}
|
||||||
|
}
|
77
src/main/java/de/marhali/easyi18n/data/LocalizedNode.java
Normal file
77
src/main/java/de/marhali/easyi18n/data/LocalizedNode.java
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package de.marhali.easyi18n.data;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.util.MapUtil;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents structured tree view for translated messages.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class LocalizedNode {
|
||||||
|
|
||||||
|
public static final String ROOT_KEY = "root";
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final String key;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private TreeMap<String, LocalizedNode> children;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private Map<String, String> value;
|
||||||
|
|
||||||
|
public LocalizedNode(@NotNull String key, @NotNull List<LocalizedNode> children) {
|
||||||
|
this.key = key;
|
||||||
|
this.children = MapUtil.convertToTreeMap(children);
|
||||||
|
this.value = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalizedNode(@NotNull String key, @NotNull Map<String, String> value) {
|
||||||
|
this.key = key;
|
||||||
|
this.children = new TreeMap<>();
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLeaf() {
|
||||||
|
return children.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Collection<LocalizedNode> getChildren() {
|
||||||
|
return children.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable LocalizedNode getChildren(@NotNull String key) {
|
||||||
|
return children.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChildren(@NotNull LocalizedNode... children) {
|
||||||
|
this.value.clear();
|
||||||
|
this.children = MapUtil.convertToTreeMap(Arrays.asList(children));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addChildren(@NotNull LocalizedNode... children) {
|
||||||
|
this.value.clear();
|
||||||
|
Arrays.stream(children).forEach(e -> this.children.put(e.getKey(), e));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeChildren(@NotNull String key) {
|
||||||
|
this.children.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Map<String, String> getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(@NotNull Map<String, String> value) {
|
||||||
|
this.children.clear();
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
33
src/main/java/de/marhali/easyi18n/data/SettingsState.java
Normal file
33
src/main/java/de/marhali/easyi18n/data/SettingsState.java
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package de.marhali.easyi18n.data;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class SettingsState {
|
||||||
|
|
||||||
|
public static final String DEFAULT_PREVIEW_LOCALE = "en";
|
||||||
|
|
||||||
|
private String localesPath;
|
||||||
|
private String previewLocale;
|
||||||
|
|
||||||
|
public SettingsState() {}
|
||||||
|
|
||||||
|
public @Nullable String getLocalesPath() {
|
||||||
|
return localesPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocalesPath(String localesPath) {
|
||||||
|
this.localesPath = localesPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull String getPreviewLocale() {
|
||||||
|
return previewLocale != null ? previewLocale : DEFAULT_PREVIEW_LOCALE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPreviewLocale(String previewLocale) {
|
||||||
|
this.previewLocale = previewLocale;
|
||||||
|
}
|
||||||
|
}
|
92
src/main/java/de/marhali/easyi18n/data/Translations.java
Normal file
92
src/main/java/de/marhali/easyi18n/data/Translations.java
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
package de.marhali.easyi18n.data;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.util.TranslationsUtil;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Translations {
|
||||||
|
|
||||||
|
private List<String> locales;
|
||||||
|
private LocalizedNode nodes;
|
||||||
|
|
||||||
|
public Translations(List<String> locales, LocalizedNode nodes) {
|
||||||
|
this.locales = locales;
|
||||||
|
this.nodes = nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getLocales() {
|
||||||
|
return locales;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalizedNode getNodes() {
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable LocalizedNode getNode(@NotNull String fullPath) {
|
||||||
|
List<String> sections = TranslationsUtil.getSections(fullPath);
|
||||||
|
|
||||||
|
LocalizedNode node = nodes;
|
||||||
|
|
||||||
|
for(String section : sections) {
|
||||||
|
if(node == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
node = node.getChildren(section);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull LocalizedNode getOrCreateNode(@NotNull String fullPath) {
|
||||||
|
List<String> sections = TranslationsUtil.getSections(fullPath);
|
||||||
|
|
||||||
|
LocalizedNode node = nodes;
|
||||||
|
|
||||||
|
for(String section : sections) {
|
||||||
|
LocalizedNode subNode = node.getChildren(section);
|
||||||
|
|
||||||
|
if(subNode == null) {
|
||||||
|
subNode = new LocalizedNode(section, new ArrayList<>());
|
||||||
|
node.addChildren(subNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
node = subNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getFullKeys() {
|
||||||
|
List<String> keys = new ArrayList<>();
|
||||||
|
|
||||||
|
if(nodes.isLeaf()) { // Root has no children
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(LocalizedNode children : nodes.getChildren()) {
|
||||||
|
keys.addAll(getFullKeys("", children));
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getFullKeys(String parentFullPath, LocalizedNode localizedNode) {
|
||||||
|
List<String> keys = new ArrayList<>();
|
||||||
|
|
||||||
|
if(localizedNode.isLeaf()) {
|
||||||
|
keys.add(parentFullPath + (parentFullPath.isEmpty() ? "" : ".") + localizedNode.getKey());
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(LocalizedNode children : localizedNode.getChildren()) {
|
||||||
|
String childrenPath = parentFullPath + (parentFullPath.isEmpty() ? "" : ".") + localizedNode.getKey();
|
||||||
|
keys.addAll(getFullKeys(childrenPath, children));
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
}
|
31
src/main/java/de/marhali/easyi18n/io/Filer.java
Normal file
31
src/main/java/de/marhali/easyi18n/io/Filer.java
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package de.marhali.easyi18n.io;
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project;
|
||||||
|
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Singleton service for file io operations.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class Filer {
|
||||||
|
|
||||||
|
private static Filer INSTANCE;
|
||||||
|
|
||||||
|
private final Project project;
|
||||||
|
|
||||||
|
public static Filer getInstance(Project project) {
|
||||||
|
return INSTANCE == null ? INSTANCE = new Filer(project) : INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Filer(Project project) {
|
||||||
|
this.project = project;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VirtualFile getFile() {
|
||||||
|
VirtualFile vfs = LocalFileSystem.getInstance().findFileByIoFile(new File(project.getBasePath() + "/src/lang/de.json"));
|
||||||
|
return vfs;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
package de.marhali.easyi18n.io.translator;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.data.LocalizedNode;
|
||||||
|
import de.marhali.easyi18n.data.Translations;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation for JSON translation files.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class JsonTranslatorIO implements TranslatorIO {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Translations read(String directoryPath) throws IOException {
|
||||||
|
VirtualFile directory = LocalFileSystem.getInstance().findFileByIoFile(new File(directoryPath));
|
||||||
|
|
||||||
|
if(directory == null || directory.getChildren() == null) {
|
||||||
|
throw new IllegalArgumentException("Specified folder is invalid (" + directoryPath + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile[] files = directory.getChildren();
|
||||||
|
|
||||||
|
List<String> locales = new ArrayList<>();
|
||||||
|
LocalizedNode nodes = new LocalizedNode(LocalizedNode.ROOT_KEY, new ArrayList<>());
|
||||||
|
|
||||||
|
for(VirtualFile file : files) {
|
||||||
|
locales.add(file.getNameWithoutExtension());
|
||||||
|
JsonObject tree = JsonParser.parseReader(new InputStreamReader(file.getInputStream())).getAsJsonObject();
|
||||||
|
readTree(file.getNameWithoutExtension(), tree, nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Translations(locales, nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(Translations translations) {
|
||||||
|
System.out.println("TODO: save");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readTree(String locale, JsonObject json, LocalizedNode data) {
|
||||||
|
for(Map.Entry<String, JsonElement> entry : json.entrySet()) {
|
||||||
|
String key = entry.getKey();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Try to go one level deeper
|
||||||
|
JsonObject childObject = entry.getValue().getAsJsonObject();
|
||||||
|
|
||||||
|
LocalizedNode childrenNode = new LocalizedNode(key, new ArrayList<>());
|
||||||
|
data.addChildren(childrenNode);
|
||||||
|
readTree(locale, childObject, childrenNode);
|
||||||
|
|
||||||
|
} catch(IllegalStateException e) { // Reached end for this node
|
||||||
|
LocalizedNode leafNode = data.getChildren(key);
|
||||||
|
|
||||||
|
if(leafNode == null) {
|
||||||
|
leafNode = new LocalizedNode(key, new HashMap<>());
|
||||||
|
data.addChildren(leafNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> messages = leafNode.getValue();
|
||||||
|
messages.put(locale, entry.getValue().getAsString());
|
||||||
|
leafNode.setValue(messages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package de.marhali.easyi18n.io.translator;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.data.Translations;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to retrieve and save localized messages.
|
||||||
|
* Can be implemented by various standards. Such as JSON, Properties-Bundle and so on.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public interface TranslatorIO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads localized messages from the persistence layer.
|
||||||
|
* @param directoryPath The full path from the parent directory which holds the different locale files.
|
||||||
|
* @return Translations model
|
||||||
|
* Example entry: username.title => [DE:Benutzername, EN:Username]
|
||||||
|
*/
|
||||||
|
Translations read(String directoryPath) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the provided messages to the persistence layer.
|
||||||
|
* @param translations Translatons model to save
|
||||||
|
* @see #read(String) More information regards the data map
|
||||||
|
*/
|
||||||
|
void save(Translations translations);
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package de.marhali.easyi18n.model;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.data.Translations;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to communicate data changes between data store and ui components.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public interface DataSynchronizer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Propagates data changes to implementation classes.
|
||||||
|
* @param translations Updated translations model
|
||||||
|
* @param searchQuery Can be used to filter visible data. Like a search function for the full key path.
|
||||||
|
*/
|
||||||
|
void synchronize(@NotNull Translations translations, @Nullable String searchQuery);
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package de.marhali.easyi18n.model;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translated messages for a dedicated key.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class KeyedTranslation {
|
||||||
|
|
||||||
|
private String key;
|
||||||
|
private Map<String, String> translations;
|
||||||
|
|
||||||
|
public KeyedTranslation(String key, Map<String, String> translations) {
|
||||||
|
this.key = key;
|
||||||
|
this.translations = translations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKey(String key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getTranslations() {
|
||||||
|
return translations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTranslations(Map<String, String> translations) {
|
||||||
|
this.translations = translations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "KeyedTranslation{" +
|
||||||
|
"key='" + key + '\'' +
|
||||||
|
", translations=" + translations +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package de.marhali.easyi18n.model;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents update request to create a new translation.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class TranslationCreate extends TranslationUpdate {
|
||||||
|
public TranslationCreate(@NotNull KeyedTranslation translation) {
|
||||||
|
super(null, translation);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package de.marhali.easyi18n.model;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents update request to delete a existing translation.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class TranslationDelete extends TranslationUpdate {
|
||||||
|
public TranslationDelete(@NotNull KeyedTranslation translation) {
|
||||||
|
super(translation, null);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package de.marhali.easyi18n.model;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an update for a translated I18n-Key. Supports key creation, manipulation and deletion.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class TranslationUpdate {
|
||||||
|
|
||||||
|
private final @Nullable KeyedTranslation origin;
|
||||||
|
private final @Nullable KeyedTranslation change;
|
||||||
|
|
||||||
|
public TranslationUpdate(@Nullable KeyedTranslation origin, @Nullable KeyedTranslation change) {
|
||||||
|
this.origin = origin;
|
||||||
|
this.change = change;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyedTranslation getOrigin() {
|
||||||
|
return origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KeyedTranslation getChange() {
|
||||||
|
return change;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCreation() {
|
||||||
|
return origin == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDeletion() {
|
||||||
|
return change == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isKeyChange() {
|
||||||
|
return origin != null && change != null && !origin.getKey().equals(change.getKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TranslationUpdate{" +
|
||||||
|
"origin=" + origin +
|
||||||
|
", change=" + change +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,122 @@
|
|||||||
|
package de.marhali.easyi18n.model.table;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.data.LocalizedNode;
|
||||||
|
import de.marhali.easyi18n.model.KeyedTranslation;
|
||||||
|
import de.marhali.easyi18n.model.TranslationUpdate;
|
||||||
|
import de.marhali.easyi18n.data.Translations;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nls;
|
||||||
|
|
||||||
|
import javax.swing.event.TableModelListener;
|
||||||
|
import javax.swing.table.TableModel;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table model to represents localized messages.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class TableModelTranslator implements TableModel {
|
||||||
|
|
||||||
|
private final Translations translations;
|
||||||
|
private final List<String> locales;
|
||||||
|
private final List<String> fullKeys;
|
||||||
|
|
||||||
|
private final Consumer<TranslationUpdate> updater;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param translations Translations instance
|
||||||
|
* @param searchQuery Search / filter param
|
||||||
|
* @param updater Consumer which can be called on cell change / update
|
||||||
|
*/
|
||||||
|
public TableModelTranslator(Translations translations, String searchQuery, Consumer<TranslationUpdate> updater) {
|
||||||
|
this.translations = translations;
|
||||||
|
this.locales = translations.getLocales();
|
||||||
|
this.updater = updater;
|
||||||
|
|
||||||
|
List<String> fullKeys = translations.getFullKeys();
|
||||||
|
|
||||||
|
if(searchQuery != null && !searchQuery.isEmpty()) { // Filter keys by searchQuery
|
||||||
|
fullKeys.removeIf(key -> !key.startsWith(searchQuery));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fullKeys = fullKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRowCount() {
|
||||||
|
return fullKeys.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColumnCount() {
|
||||||
|
return locales.size() + 1; // Number of locales plus 1 for the Key's column
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nls
|
||||||
|
@Override
|
||||||
|
public String getColumnName(int columnIndex) {
|
||||||
|
if(columnIndex == 0) {
|
||||||
|
return "<html><b>Key</b></html>";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "<html><b>" + locales.get(columnIndex - 1) + "</b></html>";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getColumnClass(int columnIndex) {
|
||||||
|
return String.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
||||||
|
return rowIndex > 0; // Everything should be editable except the headline
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||||
|
if(columnIndex == 0) { // Keys
|
||||||
|
return fullKeys.get(rowIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
String key = fullKeys.get(rowIndex);
|
||||||
|
String locale = locales.get(columnIndex - 1);
|
||||||
|
LocalizedNode node = translations.getNode(key);
|
||||||
|
|
||||||
|
return node == null ? null : node.getValue().get(locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
|
||||||
|
String key = String.valueOf(getValueAt(rowIndex, 0));
|
||||||
|
LocalizedNode node = translations.getNode(key);
|
||||||
|
|
||||||
|
if(node == null) { // Unknown cell
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String newKey = columnIndex == 0 ? String.valueOf(aValue) : key;
|
||||||
|
Map<String, String> messages = node.getValue();
|
||||||
|
|
||||||
|
// Locale message update
|
||||||
|
if(columnIndex > 0) {
|
||||||
|
if(aValue == null || ((String) aValue).isEmpty()) {
|
||||||
|
messages.remove(locales.get(columnIndex - 1));
|
||||||
|
} else {
|
||||||
|
messages.put(locales.get(columnIndex - 1), String.valueOf(aValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TranslationUpdate update = new TranslationUpdate(new KeyedTranslation(key, messages),
|
||||||
|
new KeyedTranslation(newKey, messages));
|
||||||
|
|
||||||
|
updater.accept(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addTableModelListener(TableModelListener l) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeTableModelListener(TableModelListener l) {}
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
package de.marhali.easyi18n.model.tree;
|
||||||
|
|
||||||
|
import com.intellij.ide.projectView.PresentationData;
|
||||||
|
import com.intellij.openapi.project.Project;
|
||||||
|
import com.intellij.ui.JBColor;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.SettingsService;
|
||||||
|
import de.marhali.easyi18n.data.LocalizedNode;
|
||||||
|
import de.marhali.easyi18n.data.Translations;
|
||||||
|
import de.marhali.easyi18n.util.TranslationsUtil;
|
||||||
|
import de.marhali.easyi18n.util.UiUtil;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import javax.swing.tree.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* I18n key tree preparation.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class TreeModelTranslator extends DefaultTreeModel {
|
||||||
|
|
||||||
|
private final @NotNull Project project;
|
||||||
|
private final @NotNull Translations translations;
|
||||||
|
private final @Nullable String searchQuery;
|
||||||
|
|
||||||
|
|
||||||
|
public TreeModelTranslator(
|
||||||
|
@NotNull Project project, @NotNull Translations translations, @Nullable String searchQuery) {
|
||||||
|
super(null);
|
||||||
|
|
||||||
|
this.project = project;
|
||||||
|
this.translations = translations;
|
||||||
|
this.searchQuery = searchQuery;
|
||||||
|
|
||||||
|
setRoot(generateNodes());
|
||||||
|
}
|
||||||
|
|
||||||
|
private DefaultMutableTreeNode generateNodes() {
|
||||||
|
DefaultMutableTreeNode root = new DefaultMutableTreeNode(LocalizedNode.ROOT_KEY);
|
||||||
|
|
||||||
|
if(translations.getNodes().isLeaf()) { // Empty tree
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> searchSections = searchQuery == null ?
|
||||||
|
Collections.emptyList() : TranslationsUtil.getSections(searchQuery);
|
||||||
|
|
||||||
|
for(LocalizedNode children : translations.getNodes().getChildren()) {
|
||||||
|
generateSubNodes(root, children, new ArrayList<>(searchSections));
|
||||||
|
}
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateSubNodes(DefaultMutableTreeNode parent,
|
||||||
|
LocalizedNode localizedNode, List<String> searchSections) {
|
||||||
|
|
||||||
|
String searchKey = searchSections.isEmpty() ? null : searchSections.remove(0);
|
||||||
|
|
||||||
|
if(searchKey != null && !localizedNode.getKey().startsWith(searchKey)) { // Filter node
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(localizedNode.isLeaf()) {
|
||||||
|
String previewLocale = SettingsService.getInstance(project).getState().getPreviewLocale();
|
||||||
|
|
||||||
|
String title = localizedNode.getKey();
|
||||||
|
String sub = "(" + previewLocale + ": " + localizedNode.getValue().get(previewLocale) + ")";
|
||||||
|
String tooltip = UiUtil.generateHtmlTooltip(localizedNode.getValue());
|
||||||
|
|
||||||
|
PresentationData data = new PresentationData(title, sub, null, null);
|
||||||
|
data.setTooltip(tooltip);
|
||||||
|
|
||||||
|
if(localizedNode.getValue().size() != translations.getLocales().size()) {
|
||||||
|
data.setForcedTextForeground(JBColor.RED);
|
||||||
|
}
|
||||||
|
|
||||||
|
parent.add(new DefaultMutableTreeNode(data));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
DefaultMutableTreeNode sub = new DefaultMutableTreeNode(localizedNode.getKey());
|
||||||
|
parent.add(sub);
|
||||||
|
|
||||||
|
for(LocalizedNode children : localizedNode.getChildren()) {
|
||||||
|
generateSubNodes(sub, children, new ArrayList<>(searchSections));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
src/main/java/de/marhali/easyi18n/ui/ActionsToolbar.form
Normal file
18
src/main/java/de/marhali/easyi18n/ui/ActionsToolbar.form
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="de.marhali.easyi18n.ui.ActionsToolbar">
|
||||||
|
<grid id="27dc6" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||||
|
<margin top="0" left="0" bottom="0" right="0"/>
|
||||||
|
<constraints>
|
||||||
|
<xy x="20" y="20" width="500" height="400"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
<border type="none"/>
|
||||||
|
<children>
|
||||||
|
<vspacer id="c33fe">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
</vspacer>
|
||||||
|
</children>
|
||||||
|
</grid>
|
||||||
|
</form>
|
6
src/main/java/de/marhali/easyi18n/ui/ActionsToolbar.java
Normal file
6
src/main/java/de/marhali/easyi18n/ui/ActionsToolbar.java
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package de.marhali.easyi18n.ui;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
|
public class ActionsToolbar {
|
||||||
|
}
|
53
src/main/java/de/marhali/easyi18n/ui/action/AddAction.java
Normal file
53
src/main/java/de/marhali/easyi18n/ui/action/AddAction.java
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package de.marhali.easyi18n.ui.action;
|
||||||
|
|
||||||
|
import com.intellij.icons.AllIcons;
|
||||||
|
import com.intellij.openapi.actionSystem.AnAction;
|
||||||
|
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.WindowManager;
|
||||||
|
import de.marhali.easyi18n.ui.dialog.AddDialog;
|
||||||
|
import de.marhali.easyi18n.util.TreeUtil;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import javax.swing.tree.TreePath;
|
||||||
|
|
||||||
|
public class AddAction extends AnAction {
|
||||||
|
|
||||||
|
public AddAction() {
|
||||||
|
super("Add Translation", null, AllIcons.General.Add);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||||
|
new AddDialog(e.getProject(), detectPreKey()).showAndHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String detectPreKey() {
|
||||||
|
WindowManager manager = WindowManager.getInstance();
|
||||||
|
|
||||||
|
if(manager == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(manager.getToolWindow().getContentManager().getSelectedContent().getDisplayName().equals("TreeView")) {
|
||||||
|
|
||||||
|
TreePath path = manager.getTreeView().getTree().getSelectionPath();
|
||||||
|
|
||||||
|
if(path != null) {
|
||||||
|
return TreeUtil.getFullPath(path) + ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
} else { // Table View
|
||||||
|
|
||||||
|
int row = manager.getTableView().getTable().getSelectedRow();
|
||||||
|
|
||||||
|
if(row >= 0) {
|
||||||
|
String fullPath = String.valueOf(manager.getTableView().getTable().getValueAt(row, 0));
|
||||||
|
return fullPath + ".";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package de.marhali.easyi18n.ui.action;
|
||||||
|
|
||||||
|
import com.intellij.icons.AllIcons;
|
||||||
|
import com.intellij.openapi.actionSystem.AnAction;
|
||||||
|
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action to collapse all tree nodes with children.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class CollapseTreeViewAction extends AnAction {
|
||||||
|
|
||||||
|
private final Runnable collapseRunnable;
|
||||||
|
|
||||||
|
public CollapseTreeViewAction(Runnable collapseRunnable) {
|
||||||
|
super("Collapse Tree", null, AllIcons.Actions.Collapseall);
|
||||||
|
this.collapseRunnable = collapseRunnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||||
|
collapseRunnable.run();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package de.marhali.easyi18n.ui.action;
|
||||||
|
|
||||||
|
import com.intellij.icons.AllIcons;
|
||||||
|
import com.intellij.openapi.actionSystem.AnAction;
|
||||||
|
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action to expand the entire tree (open all nodes with children).
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class ExpandTreeViewAction extends AnAction {
|
||||||
|
|
||||||
|
private final Runnable expandRunnable;
|
||||||
|
|
||||||
|
public ExpandTreeViewAction(Runnable expandRunnable) {
|
||||||
|
super("Expand Tree", null, AllIcons.Actions.Expandall);
|
||||||
|
this.expandRunnable = expandRunnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||||
|
expandRunnable.run();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package de.marhali.easyi18n.ui.action;
|
||||||
|
|
||||||
|
import com.intellij.icons.AllIcons;
|
||||||
|
import com.intellij.openapi.actionSystem.AnAction;
|
||||||
|
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.data.DataStore;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class ReloadAction extends AnAction {
|
||||||
|
|
||||||
|
public ReloadAction() {
|
||||||
|
super("Reload From Disk", null, AllIcons.Actions.Refresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||||
|
try {
|
||||||
|
DataStore.getInstance(e.getProject()).reloadFromDisk();
|
||||||
|
} catch (IOException ioException) {
|
||||||
|
ioException.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
package de.marhali.easyi18n.ui.action;
|
||||||
|
|
||||||
|
import com.intellij.openapi.actionSystem.AnAction;
|
||||||
|
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||||
|
import com.intellij.openapi.actionSystem.Presentation;
|
||||||
|
import com.intellij.openapi.actionSystem.ex.CustomComponentAction;
|
||||||
|
import com.intellij.ui.components.JBTextField;
|
||||||
|
import com.intellij.util.ui.JBUI;
|
||||||
|
|
||||||
|
import org.jdesktop.swingx.prompt.PromptSupport;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.KeyAdapter;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class SearchAction extends AnAction implements CustomComponentAction {
|
||||||
|
|
||||||
|
private final Consumer<String> searchCallback;
|
||||||
|
private JBTextField textField;
|
||||||
|
|
||||||
|
public SearchAction(@NotNull Consumer<String> searchCallback) {
|
||||||
|
super("Search");
|
||||||
|
this.searchCallback = searchCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(@NotNull AnActionEvent e) {} // Should never be called
|
||||||
|
|
||||||
|
public void actionPerformed() {
|
||||||
|
searchCallback.accept(textField == null ? "" : textField.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull JComponent createCustomComponent(@NotNull Presentation presentation, @NotNull String place) {
|
||||||
|
textField = new JBTextField();
|
||||||
|
textField.setPreferredSize(new Dimension(160, 25));
|
||||||
|
PromptSupport.setPrompt("Search Key...", textField);
|
||||||
|
|
||||||
|
textField.addKeyListener(handleKeyListener());
|
||||||
|
textField.setBorder(JBUI.Borders.empty());
|
||||||
|
|
||||||
|
JPanel panel = new JPanel(new BorderLayout());
|
||||||
|
panel.add(textField, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyAdapter handleKeyListener() {
|
||||||
|
return new KeyAdapter() {
|
||||||
|
@Override
|
||||||
|
public void keyPressed(KeyEvent e) {
|
||||||
|
if(e.getKeyCode() == KeyEvent.VK_ENTER) {
|
||||||
|
e.consume();
|
||||||
|
actionPerformed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package de.marhali.easyi18n.ui.action;
|
||||||
|
|
||||||
|
import com.intellij.icons.AllIcons;
|
||||||
|
import com.intellij.openapi.actionSystem.AnAction;
|
||||||
|
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||||
|
import de.marhali.easyi18n.ui.dialog.SettingsDialog;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public class SettingsAction extends AnAction {
|
||||||
|
|
||||||
|
public SettingsAction() {
|
||||||
|
super("Settings", null, AllIcons.General.Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||||
|
new SettingsDialog(e.getProject()).showAndHandle();
|
||||||
|
}
|
||||||
|
}
|
100
src/main/java/de/marhali/easyi18n/ui/dialog/AddDialog.java
Normal file
100
src/main/java/de/marhali/easyi18n/ui/dialog/AddDialog.java
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package de.marhali.easyi18n.ui.dialog;
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project;
|
||||||
|
import com.intellij.openapi.ui.DialogBuilder;
|
||||||
|
import com.intellij.openapi.ui.DialogWrapper;
|
||||||
|
import com.intellij.ui.components.JBLabel;
|
||||||
|
import com.intellij.ui.components.JBScrollPane;
|
||||||
|
import com.intellij.ui.components.JBTextField;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.data.DataStore;
|
||||||
|
import de.marhali.easyi18n.model.KeyedTranslation;
|
||||||
|
import de.marhali.easyi18n.model.TranslationCreate;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.border.EtchedBorder;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class AddDialog {
|
||||||
|
|
||||||
|
private final Project project;
|
||||||
|
private String preKey;
|
||||||
|
|
||||||
|
private JBTextField keyTextField;
|
||||||
|
private Map<String, JBTextField> valueTextFields;
|
||||||
|
|
||||||
|
public AddDialog(Project project, String preKey) {
|
||||||
|
this(project);
|
||||||
|
this.preKey = preKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AddDialog(Project project) {
|
||||||
|
this.project = project;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showAndHandle() {
|
||||||
|
int code = prepare().show();
|
||||||
|
|
||||||
|
if(code == DialogWrapper.OK_EXIT_CODE) {
|
||||||
|
saveTranslation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveTranslation() {
|
||||||
|
Map<String, String> messages = new HashMap<>();
|
||||||
|
|
||||||
|
valueTextFields.forEach((k, v) -> {
|
||||||
|
if(!v.getText().isEmpty()) {
|
||||||
|
messages.put(k, v.getText());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
TranslationCreate creation = new TranslationCreate(new KeyedTranslation(keyTextField.getText(), messages));
|
||||||
|
DataStore.getInstance(project).processUpdate(creation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DialogBuilder prepare() {
|
||||||
|
JPanel rootPanel = new JPanel();
|
||||||
|
rootPanel.setLayout(new BoxLayout(rootPanel, BoxLayout.PAGE_AXIS));
|
||||||
|
|
||||||
|
JPanel keyPanel = new JPanel(new GridLayout(0, 1, 2, 2));
|
||||||
|
JBLabel keyLabel = new JBLabel("Key");
|
||||||
|
keyTextField = new JBTextField(this.preKey);
|
||||||
|
keyLabel.setLabelFor(keyTextField);
|
||||||
|
keyPanel.add(keyLabel);
|
||||||
|
keyPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
|
||||||
|
keyPanel.add(keyTextField);
|
||||||
|
rootPanel.add(keyPanel);
|
||||||
|
|
||||||
|
JPanel valuePanel = new JPanel(new GridLayout(0, 1, 2, 2));
|
||||||
|
valueTextFields = new HashMap<>();
|
||||||
|
for(String locale : DataStore.getInstance(project).getTranslations().getLocales()) {
|
||||||
|
JBLabel localeLabel = new JBLabel(locale);
|
||||||
|
JBTextField localeText = new JBTextField();
|
||||||
|
localeLabel.setLabelFor(localeText);
|
||||||
|
|
||||||
|
valuePanel.add(localeLabel);
|
||||||
|
valuePanel.add(localeText);
|
||||||
|
valueTextFields.put(locale, localeText);
|
||||||
|
}
|
||||||
|
|
||||||
|
JBScrollPane valuePane = new JBScrollPane(valuePanel);
|
||||||
|
valuePane.setBorder(BorderFactory.createTitledBorder(new EtchedBorder(), "Locales"));
|
||||||
|
rootPanel.add(valuePane);
|
||||||
|
|
||||||
|
DialogBuilder builder = new DialogBuilder();
|
||||||
|
builder.setTitle("Add Translation");
|
||||||
|
builder.removeAllActions();
|
||||||
|
builder.addOkAction();
|
||||||
|
builder.addCancelAction();
|
||||||
|
builder.setCenterPanel(rootPanel);
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
96
src/main/java/de/marhali/easyi18n/ui/dialog/EditDialog.java
Normal file
96
src/main/java/de/marhali/easyi18n/ui/dialog/EditDialog.java
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package de.marhali.easyi18n.ui.dialog;
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project;
|
||||||
|
import com.intellij.openapi.ui.DialogBuilder;
|
||||||
|
import com.intellij.openapi.ui.DialogWrapper;
|
||||||
|
import com.intellij.ui.components.JBLabel;
|
||||||
|
import com.intellij.ui.components.JBScrollPane;
|
||||||
|
import com.intellij.ui.components.JBTextField;
|
||||||
|
import de.marhali.easyi18n.data.DataStore;
|
||||||
|
import de.marhali.easyi18n.model.KeyedTranslation;
|
||||||
|
import de.marhali.easyi18n.model.TranslationDelete;
|
||||||
|
import de.marhali.easyi18n.model.TranslationUpdate;
|
||||||
|
import de.marhali.easyi18n.ui.dialog.descriptor.DeleteActionDescriptor;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.border.EtchedBorder;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class EditDialog {
|
||||||
|
|
||||||
|
private final Project project;
|
||||||
|
private final KeyedTranslation origin;
|
||||||
|
|
||||||
|
private JBTextField keyTextField;
|
||||||
|
private Map<String, JBTextField> valueTextFields;
|
||||||
|
|
||||||
|
public EditDialog(Project project, KeyedTranslation origin) {
|
||||||
|
this.project = project;
|
||||||
|
this.origin = origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showAndHandle() {
|
||||||
|
int code = prepare().show();
|
||||||
|
|
||||||
|
if(code == DialogWrapper.OK_EXIT_CODE) { // Edit
|
||||||
|
DataStore.getInstance(project).processUpdate(new TranslationUpdate(origin, getChanges()));
|
||||||
|
|
||||||
|
} else if(code == DeleteActionDescriptor.EXIT_CODE) { // Delete
|
||||||
|
DataStore.getInstance(project).processUpdate(new TranslationDelete(origin));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyedTranslation getChanges() {
|
||||||
|
Map<String, String> messages = new HashMap<>();
|
||||||
|
|
||||||
|
valueTextFields.forEach((k, v) -> {
|
||||||
|
if(!v.getText().isEmpty()) {
|
||||||
|
messages.put(k, v.getText());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return new KeyedTranslation(keyTextField.getText(), messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DialogBuilder prepare() {
|
||||||
|
JPanel rootPanel = new JPanel();
|
||||||
|
rootPanel.setLayout(new BoxLayout(rootPanel, BoxLayout.PAGE_AXIS));
|
||||||
|
|
||||||
|
JPanel keyPanel = new JPanel(new GridLayout(0, 1, 2,2));
|
||||||
|
JBLabel keyLabel = new JBLabel("Key");
|
||||||
|
keyTextField = new JBTextField(this.origin.getKey());
|
||||||
|
keyLabel.setLabelFor(keyTextField);
|
||||||
|
keyPanel.add(keyLabel);
|
||||||
|
keyPanel.add(keyTextField);
|
||||||
|
keyPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
|
||||||
|
rootPanel.add(keyPanel);
|
||||||
|
|
||||||
|
JPanel valuePanel = new JPanel(new GridLayout(0, 1, 2, 2));
|
||||||
|
valueTextFields = new HashMap<>();
|
||||||
|
for(String locale : DataStore.getInstance(project).getTranslations().getLocales()) {
|
||||||
|
JBLabel localeLabel = new JBLabel(locale);
|
||||||
|
JBTextField localeText = new JBTextField(this.origin.getTranslations().get(locale));
|
||||||
|
localeLabel.setLabelFor(localeText);
|
||||||
|
|
||||||
|
valuePanel.add(localeLabel);
|
||||||
|
valuePanel.add(localeText);
|
||||||
|
valueTextFields.put(locale, localeText);
|
||||||
|
}
|
||||||
|
|
||||||
|
JBScrollPane valuePane = new JBScrollPane(valuePanel);
|
||||||
|
valuePane.setBorder(BorderFactory.createTitledBorder(new EtchedBorder(), "Locales"));
|
||||||
|
rootPanel.add(valuePane);
|
||||||
|
|
||||||
|
DialogBuilder builder = new DialogBuilder();
|
||||||
|
builder.setTitle("Edit Translation");
|
||||||
|
builder.removeAllActions();
|
||||||
|
builder.addCancelAction();
|
||||||
|
builder.addActionDescriptor(new DeleteActionDescriptor());
|
||||||
|
builder.addOkAction();
|
||||||
|
builder.setCenterPanel(rootPanel);
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
package de.marhali.easyi18n.ui.dialog;
|
||||||
|
|
||||||
|
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
||||||
|
import com.intellij.openapi.project.Project;
|
||||||
|
import com.intellij.openapi.ui.DialogBuilder;
|
||||||
|
import com.intellij.openapi.ui.DialogWrapper;
|
||||||
|
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||||
|
import com.intellij.ui.components.JBLabel;
|
||||||
|
import com.intellij.ui.components.JBTextField;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.SettingsService;
|
||||||
|
import de.marhali.easyi18n.data.DataStore;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin configuration dialog.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class SettingsDialog {
|
||||||
|
|
||||||
|
private final Project project;
|
||||||
|
|
||||||
|
private TextFieldWithBrowseButton pathText;
|
||||||
|
private JBTextField previewText;
|
||||||
|
|
||||||
|
public SettingsDialog(Project project) {
|
||||||
|
this.project = project;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showAndHandle() {
|
||||||
|
String localesPath = SettingsService.getInstance(project).getState().getLocalesPath();
|
||||||
|
String previewLocale = SettingsService.getInstance(project).getState().getPreviewLocale();
|
||||||
|
|
||||||
|
if(prepare(localesPath, previewLocale).show() == DialogWrapper.OK_EXIT_CODE) { // Save changes
|
||||||
|
SettingsService.getInstance(project).getState().setLocalesPath(pathText.getText());
|
||||||
|
SettingsService.getInstance(project).getState().setPreviewLocale(previewText.getText());
|
||||||
|
|
||||||
|
// Reload instance
|
||||||
|
try {
|
||||||
|
DataStore.getInstance(project).reloadFromDisk();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DialogBuilder prepare(String localesPath, String previewLocale) {
|
||||||
|
JPanel rootPanel = new JPanel(new GridLayout(0, 1, 2, 2));
|
||||||
|
|
||||||
|
JBLabel pathLabel = new JBLabel("Locales directory");
|
||||||
|
pathText = new TextFieldWithBrowseButton(new JTextField(localesPath));
|
||||||
|
|
||||||
|
pathLabel.setLabelFor(pathText);
|
||||||
|
pathText.addBrowseFolderListener("Locales Directory", null, project, new FileChooserDescriptor(
|
||||||
|
false, true, false, false, false, false));
|
||||||
|
|
||||||
|
rootPanel.add(pathLabel);
|
||||||
|
rootPanel.add(pathText);
|
||||||
|
|
||||||
|
JBLabel previewLabel = new JBLabel("Preview locale");
|
||||||
|
previewText = new JBTextField(previewLocale);
|
||||||
|
previewLabel.setLabelFor(previewText);
|
||||||
|
|
||||||
|
rootPanel.add(previewLabel);
|
||||||
|
rootPanel.add(previewText);
|
||||||
|
|
||||||
|
DialogBuilder builder = new DialogBuilder();
|
||||||
|
builder.setTitle("Settings");
|
||||||
|
builder.removeAllActions();
|
||||||
|
builder.addCancelAction();
|
||||||
|
builder.addOkAction();
|
||||||
|
builder.setCenterPanel(rootPanel);
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package de.marhali.easyi18n.ui.dialog.descriptor;
|
||||||
|
|
||||||
|
import com.intellij.openapi.ui.DialogBuilder;
|
||||||
|
import com.intellij.openapi.ui.DialogWrapper;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete action which represents the delete button on the edit translation dialog.
|
||||||
|
* Action can be monitored using the exit code for the opened dialog. See EXIT_CODE.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class DeleteActionDescriptor extends AbstractAction implements DialogBuilder.ActionDescriptor {
|
||||||
|
|
||||||
|
public static final int EXIT_CODE = 10;
|
||||||
|
|
||||||
|
private DialogWrapper dialogWrapper;
|
||||||
|
|
||||||
|
public DeleteActionDescriptor() {
|
||||||
|
super("Delete");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
if(dialogWrapper != null) {
|
||||||
|
dialogWrapper.close(EXIT_CODE, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Action getAction(DialogWrapper dialogWrapper) {
|
||||||
|
this.dialogWrapper = dialogWrapper;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package de.marhali.easyi18n.ui.listener;
|
||||||
|
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.awt.event.KeyListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete (DEL) keystroke listener.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class DeleteKeyListener implements KeyListener {
|
||||||
|
|
||||||
|
private final Runnable deleteRunnable;
|
||||||
|
|
||||||
|
public DeleteKeyListener(Runnable deleteRunnable) {
|
||||||
|
this.deleteRunnable = deleteRunnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void keyTyped(KeyEvent e) {
|
||||||
|
if(e.getKeyChar() == KeyEvent.VK_DELETE) {
|
||||||
|
deleteRunnable.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void keyPressed(KeyEvent e) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void keyReleased(KeyEvent e) {}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package de.marhali.easyi18n.ui.listener;
|
||||||
|
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.event.MouseListener;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Popup click listener for awt {@link MouseListener}.
|
||||||
|
* Emits consumer defined in constructor on popup open action.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class PopupClickListener implements MouseListener {
|
||||||
|
|
||||||
|
private final Consumer<MouseEvent> callback;
|
||||||
|
|
||||||
|
public PopupClickListener(Consumer<MouseEvent> callback) {
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(MouseEvent e) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mousePressed(MouseEvent e) {
|
||||||
|
if(e.isPopupTrigger()) {
|
||||||
|
this.callback.accept(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseReleased(MouseEvent e) {
|
||||||
|
if(e.isPopupTrigger()) {
|
||||||
|
this.callback.accept(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseEntered(MouseEvent e) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseExited(MouseEvent e) {}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package de.marhali.easyi18n.ui.panel;
|
||||||
|
|
||||||
|
import com.intellij.ui.JBColor;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.table.DefaultTableCellRenderer;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to {@link DefaultTableCellRenderer} but will mark the first column red if any column is empty.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class TableRenderer extends DefaultTableCellRenderer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||||
|
Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||||
|
|
||||||
|
if(column == 0 && missesValues(row, table)) {
|
||||||
|
component.setForeground(JBColor.RED);
|
||||||
|
} else { // Reset color
|
||||||
|
component.setForeground(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean missesValues(int row, JTable table) {
|
||||||
|
int columns = table.getColumnCount();
|
||||||
|
|
||||||
|
for(int i = 1; i < columns; i++) {
|
||||||
|
Object value = table.getValueAt(row, i);
|
||||||
|
|
||||||
|
if(value == null || value.toString().isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
29
src/main/java/de/marhali/easyi18n/ui/panel/TableView.form
Normal file
29
src/main/java/de/marhali/easyi18n/ui/panel/TableView.form
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="de.marhali.easyi18n.ui.panel.TableView">
|
||||||
|
<grid id="27dc6" binding="rootPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||||
|
<margin top="0" left="0" bottom="0" right="0"/>
|
||||||
|
<constraints>
|
||||||
|
<xy x="20" y="20" width="500" height="400"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
<border type="none"/>
|
||||||
|
<children>
|
||||||
|
<grid id="6c001" layout-manager="BorderLayout" hgap="0" vgap="0">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
<border type="none"/>
|
||||||
|
<children/>
|
||||||
|
</grid>
|
||||||
|
<grid id="ea7f6" binding="containerPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
|
||||||
|
<constraints>
|
||||||
|
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
<border type="none"/>
|
||||||
|
<children/>
|
||||||
|
</grid>
|
||||||
|
</children>
|
||||||
|
</grid>
|
||||||
|
</form>
|
80
src/main/java/de/marhali/easyi18n/ui/panel/TableView.java
Normal file
80
src/main/java/de/marhali/easyi18n/ui/panel/TableView.java
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package de.marhali.easyi18n.ui.panel;
|
||||||
|
|
||||||
|
import com.intellij.openapi.project.Project;
|
||||||
|
import com.intellij.ui.components.JBScrollPane;
|
||||||
|
import com.intellij.ui.table.JBTable;
|
||||||
|
import de.marhali.easyi18n.data.DataStore;
|
||||||
|
import de.marhali.easyi18n.data.LocalizedNode;
|
||||||
|
import de.marhali.easyi18n.model.DataSynchronizer;
|
||||||
|
import de.marhali.easyi18n.data.Translations;
|
||||||
|
import de.marhali.easyi18n.model.KeyedTranslation;
|
||||||
|
import de.marhali.easyi18n.model.TranslationDelete;
|
||||||
|
import de.marhali.easyi18n.model.table.TableModelTranslator;
|
||||||
|
import de.marhali.easyi18n.ui.dialog.EditDialog;
|
||||||
|
import de.marhali.easyi18n.ui.listener.DeleteKeyListener;
|
||||||
|
import de.marhali.easyi18n.ui.listener.PopupClickListener;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
|
||||||
|
public class TableView implements DataSynchronizer {
|
||||||
|
|
||||||
|
private final Project project;
|
||||||
|
|
||||||
|
private JPanel rootPanel;
|
||||||
|
private JPanel containerPanel;
|
||||||
|
|
||||||
|
private JBTable table;
|
||||||
|
|
||||||
|
public TableView(Project project) {
|
||||||
|
this.project = project;
|
||||||
|
|
||||||
|
table = new JBTable();
|
||||||
|
table.getEmptyText().setText("Empty");
|
||||||
|
table.addMouseListener(new PopupClickListener(this::handlePopup));
|
||||||
|
table.addKeyListener(new DeleteKeyListener(handleDeleteKey()));
|
||||||
|
table.setDefaultRenderer(String.class, new TableRenderer());
|
||||||
|
|
||||||
|
containerPanel.add(new JBScrollPane(table));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handlePopup(MouseEvent e) {
|
||||||
|
int row = table.rowAtPoint(e.getPoint());
|
||||||
|
|
||||||
|
if(row >= 0) {
|
||||||
|
String fullPath = String.valueOf(table.getValueAt(row, 0));
|
||||||
|
LocalizedNode node = DataStore.getInstance(project).getTranslations().getNode(fullPath);
|
||||||
|
|
||||||
|
if(node != null) {
|
||||||
|
new EditDialog(project, new KeyedTranslation(fullPath, node.getValue())).showAndHandle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Runnable handleDeleteKey() {
|
||||||
|
return () -> {
|
||||||
|
for (int selectedRow : table.getSelectedRows()) {
|
||||||
|
String fullPath = String.valueOf(table.getValueAt(selectedRow, 0));
|
||||||
|
|
||||||
|
DataStore.getInstance(project).processUpdate(
|
||||||
|
new TranslationDelete(new KeyedTranslation(fullPath, null)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void synchronize(@NotNull Translations translations, @Nullable String searchQuery) {
|
||||||
|
table.setModel(new TableModelTranslator(translations, searchQuery, update ->
|
||||||
|
DataStore.getInstance(project).processUpdate(update)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public JPanel getRootPanel() {
|
||||||
|
return rootPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JBTable getTable() {
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
}
|
130
src/main/java/de/marhali/easyi18n/ui/panel/TestPanel.form
Normal file
130
src/main/java/de/marhali/easyi18n/ui/panel/TestPanel.form
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="de.marhali.easyi18n.ui.panel.TestPanel">
|
||||||
|
<grid id="27dc6" binding="panel1" default-binding="true" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||||
|
<margin top="10" left="10" bottom="10" right="10"/>
|
||||||
|
<constraints>
|
||||||
|
<xy x="20" y="20" width="500" height="400"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
<border type="empty"/>
|
||||||
|
<children>
|
||||||
|
<grid id="2384b" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||||
|
<margin top="0" left="0" bottom="0" right="0"/>
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
<border type="none"/>
|
||||||
|
<children>
|
||||||
|
<component id="2226" class="javax.swing.JLabel">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text value="Label"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<vspacer id="f5394">
|
||||||
|
<constraints>
|
||||||
|
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
</vspacer>
|
||||||
|
<component id="298a2" class="javax.swing.JTextField" binding="textField1" default-binding="true">
|
||||||
|
<constraints>
|
||||||
|
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||||
|
<preferred-size width="150" height="-1"/>
|
||||||
|
</grid>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
</component>
|
||||||
|
</children>
|
||||||
|
</grid>
|
||||||
|
<scrollpane id="a867e">
|
||||||
|
<constraints>
|
||||||
|
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
<border type="none"/>
|
||||||
|
<children>
|
||||||
|
<grid id="e3db9" layout-manager="GridLayoutManager" row-count="9" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||||
|
<margin top="0" left="0" bottom="0" right="0"/>
|
||||||
|
<constraints/>
|
||||||
|
<properties/>
|
||||||
|
<border type="none"/>
|
||||||
|
<children>
|
||||||
|
<component id="519ad" class="javax.swing.JLabel">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text value="Label"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<vspacer id="3f792">
|
||||||
|
<constraints>
|
||||||
|
<grid row="8" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
</vspacer>
|
||||||
|
<component id="da2f8" class="javax.swing.JTextField" binding="textField2" default-binding="true">
|
||||||
|
<constraints>
|
||||||
|
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||||
|
<preferred-size width="150" height="-1"/>
|
||||||
|
</grid>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
</component>
|
||||||
|
<component id="47bfb" class="javax.swing.JLabel">
|
||||||
|
<constraints>
|
||||||
|
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text value="Label"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="a0189" class="javax.swing.JTextField" binding="textField3" default-binding="true">
|
||||||
|
<constraints>
|
||||||
|
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||||
|
<preferred-size width="150" height="-1"/>
|
||||||
|
</grid>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
</component>
|
||||||
|
<component id="5ba8a" class="javax.swing.JLabel">
|
||||||
|
<constraints>
|
||||||
|
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text value="Label"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="79bc7" class="javax.swing.JTextField" binding="textField4" default-binding="true">
|
||||||
|
<constraints>
|
||||||
|
<grid row="5" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||||
|
<preferred-size width="150" height="-1"/>
|
||||||
|
</grid>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
</component>
|
||||||
|
<component id="532d3" class="javax.swing.JLabel">
|
||||||
|
<constraints>
|
||||||
|
<grid row="6" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text value="Label"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component id="ddee" class="javax.swing.JTextField" binding="textField5" default-binding="true">
|
||||||
|
<constraints>
|
||||||
|
<grid row="7" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||||
|
<preferred-size width="150" height="-1"/>
|
||||||
|
</grid>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
</component>
|
||||||
|
</children>
|
||||||
|
</grid>
|
||||||
|
</children>
|
||||||
|
</scrollpane>
|
||||||
|
</children>
|
||||||
|
</grid>
|
||||||
|
</form>
|
12
src/main/java/de/marhali/easyi18n/ui/panel/TestPanel.java
Normal file
12
src/main/java/de/marhali/easyi18n/ui/panel/TestPanel.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package de.marhali.easyi18n.ui.panel;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
|
public class TestPanel {
|
||||||
|
private JPanel panel1;
|
||||||
|
private JTextField textField1;
|
||||||
|
private JTextField textField2;
|
||||||
|
private JTextField textField3;
|
||||||
|
private JTextField textField4;
|
||||||
|
private JTextField textField5;
|
||||||
|
}
|
29
src/main/java/de/marhali/easyi18n/ui/panel/TreeRenderer.java
Normal file
29
src/main/java/de/marhali/easyi18n/ui/panel/TreeRenderer.java
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package de.marhali.easyi18n.ui.panel;
|
||||||
|
|
||||||
|
import com.intellij.ide.util.treeView.NodeRenderer;
|
||||||
|
import com.intellij.navigation.ItemPresentation;
|
||||||
|
import com.intellij.openapi.util.NlsSafe;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.tree.DefaultTreeCellRenderer;
|
||||||
|
import javax.swing.tree.TreeCellRenderer;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
public class TreeRenderer extends NodeRenderer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customizeCellRenderer(@NotNull JTree tree, @NlsSafe Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
|
||||||
|
super.customizeCellRenderer(tree, value, selected, expanded, leaf, row, hasFocus);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @Nullable ItemPresentation getPresentation(Object node) {
|
||||||
|
if(node instanceof ItemPresentation) {
|
||||||
|
return (ItemPresentation) node;
|
||||||
|
} else {
|
||||||
|
return super.getPresentation(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
src/main/java/de/marhali/easyi18n/ui/panel/TreeView.form
Normal file
29
src/main/java/de/marhali/easyi18n/ui/panel/TreeView.form
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="de.marhali.easyi18n.ui.panel.TreeView">
|
||||||
|
<grid id="27dc6" binding="rootPanel" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||||
|
<margin top="0" left="0" bottom="0" right="0"/>
|
||||||
|
<constraints>
|
||||||
|
<xy x="20" y="20" width="500" height="400"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
<border type="none"/>
|
||||||
|
<children>
|
||||||
|
<grid id="79692" binding="toolBarPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
<border type="none"/>
|
||||||
|
<children/>
|
||||||
|
</grid>
|
||||||
|
<grid id="4e80a" binding="containerPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
<border type="none"/>
|
||||||
|
<children/>
|
||||||
|
</grid>
|
||||||
|
</children>
|
||||||
|
</grid>
|
||||||
|
</form>
|
132
src/main/java/de/marhali/easyi18n/ui/panel/TreeView.java
Normal file
132
src/main/java/de/marhali/easyi18n/ui/panel/TreeView.java
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
package de.marhali.easyi18n.ui.panel;
|
||||||
|
|
||||||
|
import com.intellij.ide.projectView.PresentationData;
|
||||||
|
import com.intellij.openapi.actionSystem.ActionManager;
|
||||||
|
import com.intellij.openapi.actionSystem.DefaultActionGroup;
|
||||||
|
import com.intellij.openapi.project.Project;
|
||||||
|
import com.intellij.ui.treeStructure.Tree;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.data.DataStore;
|
||||||
|
import de.marhali.easyi18n.data.LocalizedNode;
|
||||||
|
import de.marhali.easyi18n.model.DataSynchronizer;
|
||||||
|
import de.marhali.easyi18n.data.Translations;
|
||||||
|
import de.marhali.easyi18n.model.KeyedTranslation;
|
||||||
|
import de.marhali.easyi18n.model.TranslationDelete;
|
||||||
|
import de.marhali.easyi18n.model.tree.TreeModelTranslator;
|
||||||
|
import de.marhali.easyi18n.ui.action.CollapseTreeViewAction;
|
||||||
|
import de.marhali.easyi18n.ui.action.ExpandTreeViewAction;
|
||||||
|
import de.marhali.easyi18n.ui.dialog.EditDialog;
|
||||||
|
import de.marhali.easyi18n.ui.listener.DeleteKeyListener;
|
||||||
|
import de.marhali.easyi18n.ui.listener.PopupClickListener;
|
||||||
|
import de.marhali.easyi18n.util.TreeUtil;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.tree.DefaultMutableTreeNode;
|
||||||
|
import javax.swing.tree.TreePath;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
|
||||||
|
public class TreeView implements DataSynchronizer {
|
||||||
|
|
||||||
|
private final Project project;
|
||||||
|
|
||||||
|
private JPanel rootPanel;
|
||||||
|
private JPanel toolBarPanel;
|
||||||
|
private JPanel containerPanel;
|
||||||
|
|
||||||
|
private Tree tree;
|
||||||
|
|
||||||
|
public TreeView(Project project) {
|
||||||
|
this.project = project;
|
||||||
|
|
||||||
|
tree = new Tree();
|
||||||
|
tree.setCellRenderer(new TreeRenderer());
|
||||||
|
tree.setRootVisible(false);
|
||||||
|
tree.getEmptyText().setText("Empty");
|
||||||
|
tree.addMouseListener(new PopupClickListener(this::handlePopup));
|
||||||
|
tree.addKeyListener(new DeleteKeyListener(handleDeleteKey()));
|
||||||
|
|
||||||
|
containerPanel.add(tree);
|
||||||
|
placeActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void placeActions() {
|
||||||
|
DefaultActionGroup group = new DefaultActionGroup("TranslationsGroup", false);
|
||||||
|
|
||||||
|
ExpandTreeViewAction expand = new ExpandTreeViewAction(expandAll());
|
||||||
|
CollapseTreeViewAction collapse = new CollapseTreeViewAction(collapseAll());
|
||||||
|
|
||||||
|
group.add(collapse);
|
||||||
|
group.add(expand);
|
||||||
|
|
||||||
|
JComponent actionToolbar = ActionManager.getInstance()
|
||||||
|
.createActionToolbar("TranslationsActions", group, false).getComponent();
|
||||||
|
|
||||||
|
toolBarPanel.add(actionToolbar);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void synchronize(@NotNull Translations translations, @Nullable String searchQuery) {
|
||||||
|
tree.setModel(new TreeModelTranslator(project, translations, searchQuery));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handlePopup(MouseEvent e) {
|
||||||
|
TreePath path = tree.getPathForLocation(e.getX(), e.getY());
|
||||||
|
|
||||||
|
if(path != null) {
|
||||||
|
DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
|
||||||
|
|
||||||
|
if(node.getUserObject() instanceof PresentationData) {
|
||||||
|
String fullPath = TreeUtil.getFullPath(path);
|
||||||
|
LocalizedNode localizedNode = DataStore.getInstance(project).getTranslations().getNode(fullPath);
|
||||||
|
|
||||||
|
if(localizedNode != null) {
|
||||||
|
new EditDialog(project,new KeyedTranslation(fullPath, localizedNode.getValue())).showAndHandle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Runnable handleDeleteKey() {
|
||||||
|
return () -> {
|
||||||
|
TreePath[] paths = tree.getSelectionPaths();
|
||||||
|
|
||||||
|
if (paths == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (TreePath path : tree.getSelectionPaths()) {
|
||||||
|
String fullPath = TreeUtil.getFullPath(path);
|
||||||
|
|
||||||
|
DataStore.getInstance(project).processUpdate(
|
||||||
|
new TranslationDelete(new KeyedTranslation(fullPath, null)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Runnable expandAll() {
|
||||||
|
return () -> {
|
||||||
|
for(int i = 0; i < tree.getRowCount(); i++) {
|
||||||
|
tree.expandRow(i);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Runnable collapseAll() {
|
||||||
|
return () -> {
|
||||||
|
for(int i = 0; i < tree.getRowCount(); i++) {
|
||||||
|
tree.collapseRow(i);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public JPanel getRootPanel() {
|
||||||
|
return rootPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tree getTree() {
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package de.marhali.easyi18n.ui.table;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nls;
|
||||||
|
|
||||||
|
import javax.swing.event.TableModelListener;
|
||||||
|
import javax.swing.table.TableModel;
|
||||||
|
|
||||||
|
public class CustomTableModel implements TableModel {
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRowCount() {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColumnCount() {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nls
|
||||||
|
@Override
|
||||||
|
public String getColumnName(int columnIndex) {
|
||||||
|
switch (columnIndex) {
|
||||||
|
case 0:
|
||||||
|
return "<html><b>key</b></html>";
|
||||||
|
case 1:
|
||||||
|
return "de";
|
||||||
|
case 2:
|
||||||
|
return "en";
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getColumnClass(int columnIndex) {
|
||||||
|
return String.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCellEditable(int rowIndex, int columnIndex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||||
|
return columnIndex == 0 ? "key" : "val";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addTableModelListener(TableModelListener l) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeTableModelListener(TableModelListener l) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
39
src/main/java/de/marhali/easyi18n/util/IOUtil.java
Normal file
39
src/main/java/de/marhali/easyi18n/util/IOUtil.java
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package de.marhali.easyi18n.util;
|
||||||
|
|
||||||
|
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
|
import de.marhali.easyi18n.io.translator.JsonTranslatorIO;
|
||||||
|
import de.marhali.easyi18n.io.translator.TranslatorIO;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class IOUtil {
|
||||||
|
|
||||||
|
public static TranslatorIO determineFormat(String directoryPath) {
|
||||||
|
VirtualFile directory = LocalFileSystem.getInstance().findFileByIoFile(new File(directoryPath));
|
||||||
|
|
||||||
|
if(directory == null || directory.getChildren() == null) {
|
||||||
|
throw new IllegalArgumentException("Specified folder is invalid (" + directoryPath + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<VirtualFile> any = Arrays.stream(directory.getChildren()).findAny();
|
||||||
|
|
||||||
|
if(!any.isPresent()) {
|
||||||
|
throw new IllegalStateException("Could not determine format");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (any.get().getFileType().getDefaultExtension().toLowerCase()) {
|
||||||
|
case "json":
|
||||||
|
return new JsonTranslatorIO();
|
||||||
|
|
||||||
|
case "properties":
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException("Unsupported format: " +
|
||||||
|
any.get().getFileType().getDefaultExtension());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
src/main/java/de/marhali/easyi18n/util/MapUtil.java
Normal file
23
src/main/java/de/marhali/easyi18n/util/MapUtil.java
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package de.marhali.easyi18n.util;
|
||||||
|
|
||||||
|
import de.marhali.easyi18n.data.LocalizedNode;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map utilities.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class MapUtil {
|
||||||
|
|
||||||
|
public static TreeMap<String, LocalizedNode> convertToTreeMap(List<LocalizedNode> list) {
|
||||||
|
TreeMap<String, LocalizedNode> map = new TreeMap<>();
|
||||||
|
|
||||||
|
for(LocalizedNode item : list) {
|
||||||
|
map.put(item.getKey(), item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
33
src/main/java/de/marhali/easyi18n/util/TranslationsUtil.java
Normal file
33
src/main/java/de/marhali/easyi18n/util/TranslationsUtil.java
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package de.marhali.easyi18n.util;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TranslationsUtil {
|
||||||
|
|
||||||
|
public static @NotNull List<String> getSections(@NotNull String path) {
|
||||||
|
if(!path.contains(".")) {
|
||||||
|
return new ArrayList<>(Collections.singletonList(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ArrayList<>(Arrays.asList(path.split("\\.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NotNull String sectionsToFullPath(@NotNull List<String> sections) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
for (String section : sections) {
|
||||||
|
if(builder.length() > 0) {
|
||||||
|
builder.append(".");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.append(section);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
35
src/main/java/de/marhali/easyi18n/util/TreeUtil.java
Normal file
35
src/main/java/de/marhali/easyi18n/util/TreeUtil.java
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package de.marhali.easyi18n.util;
|
||||||
|
|
||||||
|
import com.intellij.ide.projectView.PresentationData;
|
||||||
|
import de.marhali.easyi18n.data.LocalizedNode;
|
||||||
|
import de.marhali.easyi18n.model.tree.TreeModelTranslator;
|
||||||
|
|
||||||
|
import javax.swing.tree.DefaultMutableTreeNode;
|
||||||
|
import javax.swing.tree.TreePath;
|
||||||
|
|
||||||
|
public class TreeUtil {
|
||||||
|
|
||||||
|
public static String getFullPath(TreePath path) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
|
||||||
|
for (Object obj : path.getPath()) {
|
||||||
|
DefaultMutableTreeNode node = (DefaultMutableTreeNode) obj;
|
||||||
|
Object value = node.getUserObject();
|
||||||
|
String section = value instanceof PresentationData ?
|
||||||
|
((PresentationData) value).getPresentableText() : String.valueOf(value);
|
||||||
|
|
||||||
|
if(section == null || section.equals(LocalizedNode.ROOT_KEY)) { // Skip root node
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(builder.length() != 0) {
|
||||||
|
builder.append(".");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.append(section);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
28
src/main/java/de/marhali/easyi18n/util/UiUtil.java
Normal file
28
src/main/java/de/marhali/easyi18n/util/UiUtil.java
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package de.marhali.easyi18n.util;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User interface utilities.
|
||||||
|
* @author marhali
|
||||||
|
*/
|
||||||
|
public class UiUtil {
|
||||||
|
|
||||||
|
public static String generateHtmlTooltip(Map<String, String> messages) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
builder.append("<html>");
|
||||||
|
|
||||||
|
for(Map.Entry<String, String> entry : messages.entrySet()) {
|
||||||
|
builder.append("<b>");
|
||||||
|
builder.append(entry.getKey()).append(":");
|
||||||
|
builder.append("</b> ");
|
||||||
|
builder.append(entry.getValue());
|
||||||
|
builder.append("<br>");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.append("</html>");
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -1,21 +0,0 @@
|
|||||||
package com.github.marhali.intelliji18n
|
|
||||||
|
|
||||||
import com.intellij.AbstractBundle
|
|
||||||
import org.jetbrains.annotations.NonNls
|
|
||||||
import org.jetbrains.annotations.PropertyKey
|
|
||||||
|
|
||||||
@NonNls
|
|
||||||
private const val BUNDLE = "messages.MyBundle"
|
|
||||||
|
|
||||||
object MyBundle : AbstractBundle(BUNDLE) {
|
|
||||||
|
|
||||||
@Suppress("SpreadOperator")
|
|
||||||
@JvmStatic
|
|
||||||
fun message(@PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any) =
|
|
||||||
getMessage(key, *params)
|
|
||||||
|
|
||||||
@Suppress("SpreadOperator")
|
|
||||||
@JvmStatic
|
|
||||||
fun messagePointer(@PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any) =
|
|
||||||
getLazyMessage(key, *params)
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
package com.github.marhali.intelliji18n.listeners
|
|
||||||
|
|
||||||
import com.github.marhali.intelliji18n.services.MyProjectService
|
|
||||||
import com.intellij.openapi.components.service
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
import com.intellij.openapi.project.ProjectManagerListener
|
|
||||||
|
|
||||||
internal class MyProjectManagerListener : ProjectManagerListener {
|
|
||||||
|
|
||||||
override fun projectOpened(project: Project) {
|
|
||||||
project.service<MyProjectService>()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package com.github.marhali.intelliji18n.services
|
|
||||||
|
|
||||||
import com.github.marhali.intelliji18n.MyBundle
|
|
||||||
|
|
||||||
class MyApplicationService {
|
|
||||||
|
|
||||||
init {
|
|
||||||
println(MyBundle.message("applicationService"))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package com.github.marhali.intelliji18n.services
|
|
||||||
|
|
||||||
import com.github.marhali.intelliji18n.MyBundle
|
|
||||||
import com.intellij.openapi.project.Project
|
|
||||||
|
|
||||||
class MyProjectService(project: Project) {
|
|
||||||
|
|
||||||
init {
|
|
||||||
println(MyBundle.message("projectService", project.name))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +1,14 @@
|
|||||||
<idea-plugin>
|
<idea-plugin>
|
||||||
<id>com.github.marhali.intelliji18n</id>
|
<id>de.marhali.easyi18n</id>
|
||||||
<name>intellij-i18n</name>
|
<name>easy-i18n</name>
|
||||||
<vendor>marhali</vendor>
|
<vendor>marhali</vendor>
|
||||||
|
|
||||||
<!-- Product and plugin compatibility requirements -->
|
<!-- Product and plugin compatibility requirements -->
|
||||||
<!-- https://plugins.jetbrains.com/docs/intellij/plugin-compatibility.html -->
|
<!-- https://plugins.jetbrains.com/docs/intellij/plugin-compatibility.html -->
|
||||||
<depends>com.intellij.modules.platform</depends>
|
<depends>com.intellij.modules.lang</depends>
|
||||||
|
|
||||||
<extensions defaultExtensionNs="com.intellij">
|
<extensions defaultExtensionNs="com.intellij">
|
||||||
<applicationService serviceImplementation="com.github.marhali.intelliji18n.services.MyApplicationService"/>
|
<toolWindow id="Translator" anchor="bottom" factoryClass="de.marhali.easyi18n.TranslatorToolWindowFactory" />
|
||||||
<projectService serviceImplementation="com.github.marhali.intelliji18n.services.MyProjectService"/>
|
<projectService serviceImplementation="de.marhali.easyi18n.SettingsService" />
|
||||||
</extensions>
|
</extensions>
|
||||||
|
|
||||||
<applicationListeners>
|
|
||||||
<listener class="com.github.marhali.intelliji18n.listeners.MyProjectManagerListener"
|
|
||||||
topic="com.intellij.openapi.project.ProjectManagerListener"/>
|
|
||||||
</applicationListeners>
|
|
||||||
</idea-plugin>
|
</idea-plugin>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user