diff --git a/CHANGELOG.md b/CHANGELOG.md
index c2a2d4e..6314032 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,26 +3,36 @@
# easy-i18n Changelog
## [Unreleased]
+### Added
+- Duplicate translation values filter
+- Multiple translation filters can be used together
+
+### Changed
+- Reengineered how translation filters are applied internally
+
+### Fixed
+- Exception during batch delete
+- Translation filters keep their status across updates
## [4.0.0]
-### BREAKING CHANGES
-- Configuration rework. Existing settings will be lost and must be configured via the new configuration page
+### BREAKING CHANGES
+- Configuration rework. Existing settings will be lost and must be configured via the new configuration page
-### Added
-- Key delimiters (namespace / section) can be configured
-- Extract translation intention
-- Full language support for Java, Kotlin, JavaScript / TypeScript, Vue and PHP
-- Expand already expanded nodes after data update
-- Experimental option to force translation key folding
-- Individual icon for tool-window and lookup items
-- Dedicated configuration file (easy-i18n.xml) inside .idea folder
+### Added
+- Key delimiters (namespace / section) can be configured
+- Extract translation intention
+- Full language support for Java, Kotlin, JavaScript / TypeScript, Vue and PHP
+- Expand already expanded nodes after data update
+- Experimental option to force translation key folding
+- Individual icon for tool-window and lookup items
+- Dedicated configuration file (easy-i18n.xml) inside .idea folder
-### Changed
-- Editor assistance has been reengineered. This will affect key suggestion and annotation
-- Moved configuration dialog into own page inside IDE Settings
+### Changed
+- Editor assistance has been reengineered. This will affect key suggestion and annotation
+- Moved configuration dialog into own page inside IDE Settings
-### Fixed
-- AlreadyDisposedException on FileChangeListener after project dispose
+### Fixed
+- AlreadyDisposedException on FileChangeListener after project dispose
- Request-URL limit for error reports
## [3.2.0]
diff --git a/src/main/java/de/marhali/easyi18n/DataBus.java b/src/main/java/de/marhali/easyi18n/DataBus.java
index 2eb49d0..b501b27 100644
--- a/src/main/java/de/marhali/easyi18n/DataBus.java
+++ b/src/main/java/de/marhali/easyi18n/DataBus.java
@@ -2,8 +2,8 @@ package de.marhali.easyi18n;
import de.marhali.easyi18n.model.bus.BusListener;
import de.marhali.easyi18n.model.TranslationData;
-
import de.marhali.easyi18n.model.KeyPath;
+
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -11,7 +11,8 @@ import java.util.HashSet;
import java.util.Set;
/**
- * Data-bus which is used to distribute changes regarding translations or ui tools to the participating components.
+ * Eventbus which is mandatory to distribute changes to the participating components.
+ * For user interface related components the {@link FilteredDataBus} has a builtin solution to apply all relevant filters.
* @author marhali
*/
public class DataBus {
@@ -38,8 +39,13 @@ public class DataBus {
public BusListener propagate() {
return new BusListener() {
@Override
- public void onUpdateData(@NotNull TranslationData data) {
- listener.forEach(li -> li.onUpdateData(data));
+ public void onFilterDuplicate(boolean filter) {
+ listener.forEach(li -> li.onFilterDuplicate(filter));
+ }
+
+ @Override
+ public void onFilterIncomplete(boolean filter) {
+ listener.forEach(li -> li.onFilterIncomplete(filter));
}
@Override
@@ -53,8 +59,8 @@ public class DataBus {
}
@Override
- public void onFilterMissingTranslations(boolean filter) {
- listener.forEach(li -> li.onFilterMissingTranslations(filter));
+ public void onUpdateData(@NotNull TranslationData data) {
+ listener.forEach(li -> li.onUpdateData(data));
}
};
}
diff --git a/src/main/java/de/marhali/easyi18n/FilteredDataBus.java b/src/main/java/de/marhali/easyi18n/FilteredDataBus.java
new file mode 100644
index 0000000..8b928f3
--- /dev/null
+++ b/src/main/java/de/marhali/easyi18n/FilteredDataBus.java
@@ -0,0 +1,193 @@
+package de.marhali.easyi18n;
+
+import com.intellij.openapi.project.Project;
+
+import de.marhali.easyi18n.model.KeyPath;
+import de.marhali.easyi18n.model.TranslationData;
+import de.marhali.easyi18n.model.TranslationNode;
+import de.marhali.easyi18n.model.TranslationValue;
+import de.marhali.easyi18n.model.bus.BusListener;
+import de.marhali.easyi18n.model.bus.ExpandAllListener;
+import de.marhali.easyi18n.model.bus.FilteredBusListener;
+import de.marhali.easyi18n.settings.ProjectSettingsService;
+import de.marhali.easyi18n.settings.ProjectSettingsState;
+import de.marhali.easyi18n.util.KeyPathConverter;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Consumer;
+
+/**
+ * UI related eventbus. Uses the {@link BusListener} by {@link DataBus} under the hood.
+ * User-Interface components (e.g. tabs) use this component by implementing {@link FilteredBusListener}.
+ * @author marhali
+ */
+public class FilteredDataBus implements BusListener {
+
+ private final Project project;
+ private final Set listener;
+
+ private TranslationData data;
+ private ProjectSettingsState settings;
+ private boolean filterDuplicate;
+ private boolean filterIncomplete;
+ private String searchQuery;
+ private KeyPath focusKey;
+
+ /**
+ * Constructs a new project specific UI eventbus.
+ * @param project Associated project
+ */
+ public FilteredDataBus(@NotNull Project project) {
+ this.project = project;
+ this.listener = new HashSet<>();
+ }
+
+ /**
+ * Adds a participant to the event bus. Every participant needs to be added manually.
+ * @param listener Bus listener
+ */
+ public void addListener(FilteredBusListener listener) {
+ this.listener.add(listener);
+ }
+
+ @Override
+ public void onFilterDuplicate(boolean filter) {
+ this.filterDuplicate = filter;
+ this.processAndPropagate();
+ fire(ExpandAllListener::onExpandAll);
+ }
+
+ @Override
+ public void onFilterIncomplete(boolean filter) {
+ this.filterIncomplete = filter;
+ this.processAndPropagate();
+ fire(ExpandAllListener::onExpandAll);
+ }
+
+ @Override
+ public void onFocusKey(@NotNull KeyPath key) {
+ this.focusKey = key;
+ fire(li -> li.onFocusKey(key));
+ }
+
+ @Override
+ public void onSearchQuery(@Nullable String query) {
+ this.searchQuery = query == null ? null : query.toLowerCase();
+ this.processAndPropagate();
+ fire(ExpandAllListener::onExpandAll);
+ }
+
+ @Override
+ public void onUpdateData(@NotNull TranslationData data) {
+ this.data = data;
+ this.settings = ProjectSettingsService.get(this.project).getState();
+ processAndPropagate();
+ }
+
+ /**
+ * Filter translations based on supplied filters and propagate changes
+ * to all registered participants.
+ * Internally creates a shallow copy of the cached translations and
+ * removes any that does not apply with the configured filters.
+ */
+ private void processAndPropagate() {
+ TranslationData shadow = new TranslationData(
+ this.data.getLocales(), new TranslationNode(this.data.isSorting()));
+
+ for (KeyPath key : this.data.getFullKeys()) {
+ TranslationValue value = this.data.getTranslation(key);
+ assert value != null;
+
+ // We create a shallow copy of the current translation instance
+ // and remove every translation that does not conform with the configured filter/s
+ shadow.setTranslation(key, value);
+
+ // filter incomplete translations
+ if(filterIncomplete) {
+ if(!isIncomplete(value)) {
+ shadow.setTranslation(key, null);
+ }
+ }
+
+ // filter duplicate values
+ if(filterDuplicate) {
+ if(!isDuplicate(value)) {
+ shadow.setTranslation(key, null);
+ }
+ }
+
+ // full-text-search
+ if(searchQuery != null) {
+ if(!isSearched(key, value)) {
+ shadow.setTranslation(key, null);
+ }
+ }
+ }
+
+ fire(li -> {
+ li.onUpdateData(shadow);
+
+ if(focusKey != null) {
+ li.onFocusKey(focusKey);
+ }
+ });
+ }
+
+ /**
+ * @param li Notify all registered participants about an fired event.
+ */
+ private void fire(@NotNull Consumer li) {
+ listener.forEach(li);
+ }
+
+ /**
+ * Filter translations with missing translation values for any locale
+ */
+ private boolean isIncomplete(@NotNull TranslationValue value) {
+ return this.data.getLocales().size() != value.getLocaleContents().size();
+ }
+
+ /**
+ * Filter duplicate translation values
+ */
+ private boolean isDuplicate(@NotNull TranslationValue value) {
+ Collection contents = value.getLocaleContents();
+
+ for (KeyPath currentKey : this.data.getFullKeys()) {
+ TranslationValue currentValue = this.data.getTranslation(currentKey);
+ assert currentValue != null;
+
+ for (String currentContent : currentValue.getLocaleContents()) {
+ if(contents.contains(currentContent)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Filter by search query
+ */
+ private boolean isSearched(@NotNull KeyPath key, @NotNull TranslationValue value) {
+ String concatKey = new KeyPathConverter(settings).toString(key).toLowerCase();
+
+ if(searchQuery.contains(concatKey) || concatKey.contains(searchQuery)) {
+ return true;
+ }
+
+ for (String localeContent : value.getLocaleContents()) {
+ if(localeContent.toLowerCase().contains(searchQuery)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/de/marhali/easyi18n/InstanceManager.java b/src/main/java/de/marhali/easyi18n/InstanceManager.java
index 5fe9c55..829c60a 100644
--- a/src/main/java/de/marhali/easyi18n/InstanceManager.java
+++ b/src/main/java/de/marhali/easyi18n/InstanceManager.java
@@ -20,6 +20,7 @@ public class InstanceManager {
private final DataStore store;
private final DataBus bus;
+ private final FilteredDataBus uiBus;
public static InstanceManager get(@NotNull Project project) {
InstanceManager instance = INSTANCES.get(project);
@@ -35,6 +36,10 @@ public class InstanceManager {
private InstanceManager(@NotNull Project project) {
this.store = new DataStore(project);
this.bus = new DataBus();
+ this.uiBus = new FilteredDataBus(project);
+
+ // Register ui eventbus on top of the normal eventbus
+ this.bus.addListener(this.uiBus);
// Load data after first initialization
ApplicationManager.getApplication().invokeLater(() -> {
@@ -48,10 +53,20 @@ public class InstanceManager {
return this.store;
}
+ /**
+ * Primary eventbus.
+ */
public DataBus bus() {
return this.bus;
}
+ /**
+ * UI optimized eventbus with builtin filter logic.
+ */
+ public FilteredDataBus uiBus() {
+ return this.uiBus;
+ }
+
/**
* Reloads the plugin instance. Unsaved cached data will be deleted.
* Fetches data from persistence layer and notifies all endpoints via {@link DataBus}.
diff --git a/src/main/java/de/marhali/easyi18n/action/FilterDuplicateAction.java b/src/main/java/de/marhali/easyi18n/action/FilterDuplicateAction.java
new file mode 100644
index 0000000..f07a460
--- /dev/null
+++ b/src/main/java/de/marhali/easyi18n/action/FilterDuplicateAction.java
@@ -0,0 +1,30 @@
+package de.marhali.easyi18n.action;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.Project;
+import de.marhali.easyi18n.InstanceManager;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+import java.util.ResourceBundle;
+
+/**
+ * Action to toggle duplicate translation values filter.
+ * @author marhali
+ */
+public class FilterDuplicateAction extends AnAction {
+ // TODO: Custom icon to differentiate between incomplete and duplicate filter
+ public FilterDuplicateAction() {
+ super(ResourceBundle.getBundle("messages").getString("action.filter.duplicate"),
+ null, AllIcons.General.ShowWarning);
+ }
+ @Override
+ public void actionPerformed(@NotNull AnActionEvent e) {
+ Project project = Objects.requireNonNull(e.getProject());
+ boolean enable = e.getPresentation().getIcon() == AllIcons.General.ShowWarning;
+ e.getPresentation().setIcon(enable ? AllIcons.General.Warning : AllIcons.General.ShowWarning);
+ InstanceManager.get(project).bus().propagate().onFilterDuplicate(enable);
+ }
+}
diff --git a/src/main/java/de/marhali/easyi18n/action/FilterMissingTranslationsAction.java b/src/main/java/de/marhali/easyi18n/action/FilterIncompleteAction.java
similarity index 76%
rename from src/main/java/de/marhali/easyi18n/action/FilterMissingTranslationsAction.java
rename to src/main/java/de/marhali/easyi18n/action/FilterIncompleteAction.java
index 1e13dea..4133c06 100644
--- a/src/main/java/de/marhali/easyi18n/action/FilterMissingTranslationsAction.java
+++ b/src/main/java/de/marhali/easyi18n/action/FilterIncompleteAction.java
@@ -14,9 +14,10 @@ import java.util.ResourceBundle;
* Action which toggles translation filter on missing values.
* @author marhali
*/
-public class FilterMissingTranslationsAction extends AnAction {
- public FilterMissingTranslationsAction() {
- super(ResourceBundle.getBundle("messages").getString("action.toggle-missing"),
+public class FilterIncompleteAction extends AnAction {
+ // TODO: Custom icon to differentiate between incomplete and duplicate filter
+ public FilterIncompleteAction() {
+ super(ResourceBundle.getBundle("messages").getString("action.filter.incomplete"),
null, AllIcons.General.ShowWarning);
}
@@ -25,6 +26,6 @@ public class FilterMissingTranslationsAction extends AnAction {
Project project = Objects.requireNonNull(e.getProject());
boolean enable = e.getPresentation().getIcon() == AllIcons.General.ShowWarning;
e.getPresentation().setIcon(enable ? AllIcons.General.Warning : AllIcons.General.ShowWarning);
- InstanceManager.get(project).bus().propagate().onFilterMissingTranslations(enable);
+ InstanceManager.get(project).bus().propagate().onFilterIncomplete(enable);
}
}
\ No newline at end of file
diff --git a/src/main/java/de/marhali/easyi18n/model/bus/ExpandAllListener.java b/src/main/java/de/marhali/easyi18n/model/bus/ExpandAllListener.java
new file mode 100644
index 0000000..f5d8236
--- /dev/null
+++ b/src/main/java/de/marhali/easyi18n/model/bus/ExpandAllListener.java
@@ -0,0 +1,13 @@
+package de.marhali.easyi18n.model.bus;
+
+/**
+ * Single event listener.
+ * @see #onExpandAll()
+ * @author marhali
+ */
+public interface ExpandAllListener {
+ /**
+ * Action to expand all nodes
+ */
+ void onExpandAll();
+}
\ No newline at end of file
diff --git a/src/main/java/de/marhali/easyi18n/model/bus/FilteredBusListener.java b/src/main/java/de/marhali/easyi18n/model/bus/FilteredBusListener.java
new file mode 100644
index 0000000..1a533c4
--- /dev/null
+++ b/src/main/java/de/marhali/easyi18n/model/bus/FilteredBusListener.java
@@ -0,0 +1,9 @@
+package de.marhali.easyi18n.model.bus;
+
+import de.marhali.easyi18n.FilteredDataBus;
+
+/**
+ * Interface to replicate the state of {@link FilteredDataBus} to underlying components.
+ * @author marhali
+ */
+public interface FilteredBusListener extends UpdateDataListener, FocusKeyListener, ExpandAllListener {}
diff --git a/src/main/java/de/marhali/easyi18n/service/TranslatorToolWindowFactory.java b/src/main/java/de/marhali/easyi18n/service/TranslatorToolWindowFactory.java
index d0837fa..3bbfd23 100644
--- a/src/main/java/de/marhali/easyi18n/service/TranslatorToolWindowFactory.java
+++ b/src/main/java/de/marhali/easyi18n/service/TranslatorToolWindowFactory.java
@@ -27,6 +27,7 @@ public class TranslatorToolWindowFactory implements ToolWindowFactory, DumbAware
@Override
public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {
+ InstanceManager manager = InstanceManager.get(project);
ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();
// Translations tree view
@@ -46,19 +47,19 @@ public class TranslatorToolWindowFactory implements ToolWindowFactory, DumbAware
// ToolWindow Actions (Can be used for every view)
List actions = new ArrayList<>();
actions.add(new AddAction());
- actions.add(new FilterMissingTranslationsAction());
+ actions.add(new FilterIncompleteAction());
+ actions.add(new FilterDuplicateAction());
actions.add(new ReloadAction());
actions.add(new SettingsAction());
- actions.add(new SearchAction((query) -> InstanceManager.get(project).bus().propagate().onSearchQuery(query)));
+ actions.add(new SearchAction((query) -> manager.bus().propagate().onSearchQuery(query)));
toolWindow.setTitleActions(actions);
// Initialize Window Manager
WindowManager.getInstance().initialize(toolWindow, treeView, tableView);
// Synchronize ui with underlying data
- InstanceManager manager = InstanceManager.get(project);
- manager.bus().addListener(treeView);
- manager.bus().addListener(tableView);
+ manager.uiBus().addListener(treeView);
+ manager.uiBus().addListener(tableView);
manager.bus().propagate().onUpdateData(manager.store().getData());
}
}
\ No newline at end of file
diff --git a/src/main/java/de/marhali/easyi18n/tabs/TableView.java b/src/main/java/de/marhali/easyi18n/tabs/TableView.java
index 51b65de..658e65b 100644
--- a/src/main/java/de/marhali/easyi18n/tabs/TableView.java
+++ b/src/main/java/de/marhali/easyi18n/tabs/TableView.java
@@ -11,33 +11,30 @@ import de.marhali.easyi18n.listener.DeleteKeyListener;
import de.marhali.easyi18n.listener.PopupClickListener;
import de.marhali.easyi18n.model.TranslationData;
import de.marhali.easyi18n.model.action.TranslationDelete;
-import de.marhali.easyi18n.model.bus.BusListener;
import de.marhali.easyi18n.model.KeyPath;
import de.marhali.easyi18n.model.Translation;
import de.marhali.easyi18n.model.TranslationValue;
+import de.marhali.easyi18n.model.bus.FilteredBusListener;
import de.marhali.easyi18n.renderer.TableRenderer;
import de.marhali.easyi18n.tabs.mapper.TableModelMapper;
import de.marhali.easyi18n.util.KeyPathConverter;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
-import java.util.ResourceBundle;
+import java.util.*;
/**
* Shows translation state as table.
- *
* @author marhali
*/
-public class TableView implements BusListener {
+public class TableView implements FilteredBusListener {
private final JBTable table;
private final Project project;
- private TableModelMapper currentMapper;
private KeyPathConverter converter;
private JPanel rootPanel;
@@ -70,12 +67,14 @@ public class TableView implements BusListener {
}
private void deleteSelectedRows() {
- for (int selectedRow : table.getSelectedRows()) {
- KeyPath fullPath = this.converter.fromString(String.valueOf(table.getValueAt(selectedRow, 0)));
+ Set batchDelete = new HashSet<>();
- InstanceManager.get(project).processUpdate(
- new TranslationDelete(new Translation(fullPath, null))
- );
+ for (int selectedRow : table.getSelectedRows()) {
+ batchDelete.add(this.converter.fromString(String.valueOf(table.getValueAt(selectedRow, 0))));
+ }
+
+ for (KeyPath key : batchDelete) {
+ InstanceManager.get(project).processUpdate(new TranslationDelete(new Translation(key, null)));
}
}
@@ -83,7 +82,7 @@ public class TableView implements BusListener {
public void onUpdateData(@NotNull TranslationData data) {
this.converter = new KeyPathConverter(project);
- table.setModel(this.currentMapper = new TableModelMapper(data, this.converter, update ->
+ table.setModel(new TableModelMapper(data, this.converter, update ->
InstanceManager.get(project).processUpdate(update)));
}
@@ -105,19 +104,8 @@ public class TableView implements BusListener {
}
@Override
- public void onSearchQuery(@Nullable String query) {
- if (this.currentMapper != null) {
- this.currentMapper.onSearchQuery(query);
- this.table.updateUI();
- }
- }
-
- @Override
- public void onFilterMissingTranslations(boolean filter) {
- if(this.currentMapper != null) {
- this.currentMapper.onFilterMissingTranslations(filter);
- this.table.updateUI();
- }
+ public void onExpandAll() {
+ // table-view never collapse any rows
}
public JPanel getRootPanel() {
diff --git a/src/main/java/de/marhali/easyi18n/tabs/TreeView.java b/src/main/java/de/marhali/easyi18n/tabs/TreeView.java
index 7db7850..5f466e3 100644
--- a/src/main/java/de/marhali/easyi18n/tabs/TreeView.java
+++ b/src/main/java/de/marhali/easyi18n/tabs/TreeView.java
@@ -13,7 +13,6 @@ import de.marhali.easyi18n.dialog.EditDialog;
import de.marhali.easyi18n.listener.ReturnKeyListener;
import de.marhali.easyi18n.model.TranslationData;
import de.marhali.easyi18n.model.action.TranslationDelete;
-import de.marhali.easyi18n.model.bus.BusListener;
import de.marhali.easyi18n.action.treeview.CollapseTreeViewAction;
import de.marhali.easyi18n.action.treeview.ExpandTreeViewAction;
import de.marhali.easyi18n.listener.DeleteKeyListener;
@@ -21,6 +20,7 @@ import de.marhali.easyi18n.listener.PopupClickListener;
import de.marhali.easyi18n.model.KeyPath;
import de.marhali.easyi18n.model.Translation;
import de.marhali.easyi18n.model.TranslationValue;
+import de.marhali.easyi18n.model.bus.FilteredBusListener;
import de.marhali.easyi18n.renderer.TreeRenderer;
import de.marhali.easyi18n.settings.ProjectSettingsService;
import de.marhali.easyi18n.tabs.mapper.TreeModelMapper;
@@ -32,16 +32,13 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.ResourceBundle;
+import java.util.*;
/**
* Show translation state as tree.
- *
* @author marhali
*/
-public class TreeView implements BusListener {
+public class TreeView implements FilteredBusListener {
private final Tree tree;
@@ -71,8 +68,8 @@ public class TreeView implements BusListener {
private void placeActions() {
DefaultActionGroup group = new DefaultActionGroup("TranslationsGroup", false);
- ExpandTreeViewAction expand = new ExpandTreeViewAction(this::expandAll);
- CollapseTreeViewAction collapse = new CollapseTreeViewAction(this::collapseAll);
+ ExpandTreeViewAction expand = new ExpandTreeViewAction(this::onExpandAll);
+ CollapseTreeViewAction collapse = new CollapseTreeViewAction(this::onCollapseAll);
group.add(collapse);
group.add(expand);
@@ -117,24 +114,6 @@ public class TreeView implements BusListener {
}
}
- @Override
- public void onSearchQuery(@Nullable String query) {
- if (this.currentMapper != null) {
- this.currentMapper.onSearchQuery(query);
- this.expandAll();
- this.tree.updateUI();
- }
- }
-
- @Override
- public void onFilterMissingTranslations(boolean filter) {
- if (this.currentMapper != null) {
- this.currentMapper.onFilterMissingTranslations(filter);
- this.expandAll();
- this.tree.updateUI();
- }
- }
-
private void showEditPopup(@Nullable TreePath path) {
if (path == null) {
return;
@@ -157,28 +136,30 @@ public class TreeView implements BusListener {
}
private void deleteSelectedNodes() {
- TreePath[] paths = tree.getSelectionPaths();
+ TreePath[] selection = tree.getSelectionPaths();
+ Set batchDelete = new HashSet<>();
- if (paths == null) {
+ if(selection == null) {
return;
}
- for (TreePath path : tree.getSelectionPaths()) {
- KeyPath fullPath = TreeUtil.getFullPath(path);
+ for (TreePath path : selection) {
+ batchDelete.add(TreeUtil.getFullPath(path));
+ }
- InstanceManager.get(project).processUpdate(
- new TranslationDelete(new Translation(fullPath, null))
- );
+ for (KeyPath key : batchDelete) {
+ InstanceManager.get(project).processUpdate(new TranslationDelete(new Translation(key, null)));
}
}
- private void expandAll() {
+ @Override
+ public void onExpandAll() {
for (int i = 0; i < tree.getRowCount(); i++) {
tree.expandRow(i);
}
}
- private void collapseAll() {
+ public void onCollapseAll() {
for (int i = 0; i < tree.getRowCount(); i++) {
tree.collapseRow(i);
}
diff --git a/src/main/java/de/marhali/easyi18n/tabs/mapper/TableModelMapper.java b/src/main/java/de/marhali/easyi18n/tabs/mapper/TableModelMapper.java
index 2a049a6..98d1895 100644
--- a/src/main/java/de/marhali/easyi18n/tabs/mapper/TableModelMapper.java
+++ b/src/main/java/de/marhali/easyi18n/tabs/mapper/TableModelMapper.java
@@ -2,8 +2,6 @@ package de.marhali.easyi18n.tabs.mapper;
import de.marhali.easyi18n.model.TranslationData;
import de.marhali.easyi18n.model.action.TranslationUpdate;
-import de.marhali.easyi18n.model.bus.FilterIncompleteListener;
-import de.marhali.easyi18n.model.bus.SearchQueryListener;
import de.marhali.easyi18n.model.KeyPath;
import de.marhali.easyi18n.model.Translation;
import de.marhali.easyi18n.model.TranslationValue;
@@ -11,7 +9,6 @@ import de.marhali.easyi18n.util.KeyPathConverter;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;
@@ -23,13 +20,13 @@ import java.util.function.Consumer;
* Mapping {@link TranslationData} to {@link TableModel}.
* @author marhali
*/
-public class TableModelMapper implements TableModel, SearchQueryListener, FilterIncompleteListener {
+public class TableModelMapper implements TableModel {
private final @NotNull TranslationData data;
private final @NotNull KeyPathConverter converter;
private final @NotNull List locales;
- private @NotNull List fullKeys;
+ private final @NotNull List fullKeys;
private final @NotNull Consumer updater;
@@ -44,49 +41,6 @@ public class TableModelMapper implements TableModel, SearchQueryListener, Filter
this.updater = updater;
}
- @Override
- public void onSearchQuery(@Nullable String query) {
- if(query == null) { // Reset
- this.fullKeys = new ArrayList<>(this.data.getFullKeys());
- return;
- }
-
- query = query.toLowerCase();
- List matches = new ArrayList<>();
-
- for(KeyPath key : this.data.getFullKeys()) {
- if(this.converter.toString(key).toLowerCase().contains(query)) {
- matches.add(key);
- } else {
- for(String content : this.data.getTranslation(key).getLocaleContents()) {
- if(content.toLowerCase().contains(query)) {
- matches.add(key);
- }
- }
- }
- }
-
- this.fullKeys = matches;
- }
-
- @Override
- public void onFilterMissingTranslations(boolean filter) {
- if(!filter) { // Reset
- this.fullKeys = new ArrayList<>(this.data.getFullKeys());
- return;
- }
-
- List matches = new ArrayList<>();
-
- for(KeyPath key : this.data.getFullKeys()) {
- if(this.data.getTranslation(key).getLocaleContents().size() != this.locales.size()) {
- matches.add(key);
- }
- }
-
- this.fullKeys = matches;
- }
-
@Override
public int getRowCount() {
return this.fullKeys.size();
diff --git a/src/main/java/de/marhali/easyi18n/tabs/mapper/TreeModelMapper.java b/src/main/java/de/marhali/easyi18n/tabs/mapper/TreeModelMapper.java
index 18279dd..caa9580 100644
--- a/src/main/java/de/marhali/easyi18n/tabs/mapper/TreeModelMapper.java
+++ b/src/main/java/de/marhali/easyi18n/tabs/mapper/TreeModelMapper.java
@@ -5,12 +5,8 @@ import com.intellij.ui.JBColor;
import de.marhali.easyi18n.model.TranslationData;
import de.marhali.easyi18n.model.TranslationNode;
-import de.marhali.easyi18n.model.bus.FilterIncompleteListener;
-import de.marhali.easyi18n.model.bus.SearchQueryListener;
import de.marhali.easyi18n.model.KeyPath;
-import de.marhali.easyi18n.model.TranslationValue;
import de.marhali.easyi18n.settings.ProjectSettings;
-import de.marhali.easyi18n.util.KeyPathConverter;
import de.marhali.easyi18n.util.UiUtil;
import org.jetbrains.annotations.NotNull;
@@ -25,17 +21,15 @@ import java.util.Map;
* Mapping {@link TranslationData} to {@link TreeModel}.
* @author marhali
*/
-public class TreeModelMapper extends DefaultTreeModel implements SearchQueryListener, FilterIncompleteListener {
+public class TreeModelMapper extends DefaultTreeModel {
private final TranslationData data;
- private final KeyPathConverter converter;
private final ProjectSettings state;
public TreeModelMapper(TranslationData data, ProjectSettings state) {
super(null);
this.data = data;
- this.converter = new KeyPathConverter(state);
this.state = state;
DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
@@ -43,65 +37,7 @@ public class TreeModelMapper extends DefaultTreeModel implements SearchQueryList
super.setRoot(rootNode);
}
- @Override
- public void onSearchQuery(@Nullable String query) {
- DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
- TranslationData shadow = new TranslationData(this.state.isSorting());
-
- if(query == null) { // Reset
- this.generateNodes(rootNode, this.data.getRootNode());
- super.setRoot(rootNode);
- return;
- }
-
- query = query.toLowerCase();
-
- for(KeyPath currentKey : this.data.getFullKeys()) {
- TranslationValue translation = this.data.getTranslation(currentKey);
- String loweredKey = this.converter.toString(currentKey).toLowerCase();
-
- if(query.contains(loweredKey) || loweredKey.contains(query)) {
- shadow.setTranslation(currentKey, translation);
- continue;
- }
-
- for(String currentContent : translation.getLocaleContents()) {
- if(currentContent.toLowerCase().contains(query)) {
- shadow.setTranslation(currentKey, translation);
- break;
- }
- }
- }
-
- this.generateNodes(rootNode, shadow.getRootNode());
- super.setRoot(rootNode);
- }
-
- @Override
- public void onFilterMissingTranslations(boolean filter) {
- DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
- TranslationData shadow = new TranslationData(this.state.isSorting());
-
- if(!filter) { // Reset
- this.generateNodes(rootNode, this.data.getRootNode());
- super.setRoot(rootNode);
- return;
- }
-
- for(KeyPath currentKey : this.data.getFullKeys()) {
- TranslationValue translation = this.data.getTranslation(currentKey);
-
- if(translation.getLocaleContents().size() != this.data.getLocales().size()) {
- shadow.setTranslation(currentKey, translation);
- }
- }
-
- this.generateNodes(rootNode, shadow.getRootNode());
- super.setRoot(rootNode);
- }
-
/**
- *
* @param parent Parent tree node
* @param translationNode Layer of translation node to write to tree
* @return true if children nodes misses any translation values
diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties
index 3df89be..93af07e 100644
--- a/src/main/resources/messages.properties
+++ b/src/main/resources/messages.properties
@@ -6,7 +6,8 @@ view.table.title=Table View
view.empty=No translations found. Click on settings to specify a locales directory or reload
action.add=Add Translation
action.edit=Edit Translation
-action.toggle-missing=Toggle missing translations
+action.filter.incomplete=Filter Incomplete Translations
+action.filter.duplicate=Filter Duplicate Translation Values
action.reload=Reload From Disk
action.settings=Settings
action.search=Search...