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...