diff --git a/src/main/java/org/jabref/gui/preferences/NameFormatterItemModel.java b/src/main/java/org/jabref/gui/preferences/NameFormatterItemModel.java
new file mode 100644
index 00000000000..721407bc3b3
--- /dev/null
+++ b/src/main/java/org/jabref/gui/preferences/NameFormatterItemModel.java
@@ -0,0 +1,45 @@
+package org.jabref.gui.preferences;
+
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
+import org.jabref.logic.layout.format.NameFormatter;
+
+public class NameFormatterItemModel {
+ private final StringProperty name = new SimpleStringProperty("");
+ private final StringProperty format = new SimpleStringProperty("");
+
+ NameFormatterItemModel() { this(""); }
+
+ NameFormatterItemModel(String name) {
+ this(name, NameFormatter.DEFAULT_FORMAT);
+ }
+
+ NameFormatterItemModel(String name, String format) {
+ this.name.setValue(name);
+ this.format.setValue(format);
+ }
+
+ public void setName(String name) {
+ this.name.setValue(name);
+ }
+
+ public String getName() {
+ return name.getValue();
+ }
+
+ public StringProperty nameProperty() { return name; }
+
+ public void setFormat(String format) {
+ this.format.setValue(format);
+ }
+
+ public String getFormat() {
+ return format.getValue();
+ }
+
+ public StringProperty formatProperty() { return format; }
+
+ @Override
+ public String toString() { return "[" + name.getValue() + "," + format.getValue() + "]"; }
+}
diff --git a/src/main/java/org/jabref/gui/preferences/NameFormatterTab.fxml b/src/main/java/org/jabref/gui/preferences/NameFormatterTab.fxml
new file mode 100644
index 00000000000..54cf2632d98
--- /dev/null
+++ b/src/main/java/org/jabref/gui/preferences/NameFormatterTab.fxml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/org/jabref/gui/preferences/NameFormatterTab.java b/src/main/java/org/jabref/gui/preferences/NameFormatterTab.java
deleted file mode 100644
index 94d7e84c12b..00000000000
--- a/src/main/java/org/jabref/gui/preferences/NameFormatterTab.java
+++ /dev/null
@@ -1,233 +0,0 @@
-package org.jabref.gui.preferences;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
-
-import javafx.beans.property.SimpleStringProperty;
-import javafx.collections.FXCollections;
-import javafx.collections.ObservableList;
-import javafx.scene.Node;
-import javafx.scene.control.Button;
-import javafx.scene.control.Label;
-import javafx.scene.control.ScrollPane;
-import javafx.scene.control.TableColumn;
-import javafx.scene.control.TableView;
-import javafx.scene.control.TextField;
-import javafx.scene.control.cell.PropertyValueFactory;
-import javafx.scene.control.cell.TextFieldTableCell;
-import javafx.scene.layout.BorderPane;
-import javafx.scene.layout.GridPane;
-import javafx.scene.layout.HBox;
-import javafx.scene.layout.Pane;
-
-import org.jabref.gui.actions.ActionFactory;
-import org.jabref.gui.actions.StandardActions;
-import org.jabref.gui.help.HelpAction;
-import org.jabref.logic.help.HelpFile;
-import org.jabref.logic.l10n.Localization;
-import org.jabref.logic.layout.format.NameFormatter;
-import org.jabref.preferences.JabRefPreferences;
-
-public class NameFormatterTab extends Pane implements PreferencesTab {
-
- private final JabRefPreferences prefs;
- private boolean tableChanged;
- private final TableView table;
- private final GridPane builder = new GridPane();
- private final List tableRows = new ArrayList<>(10);
- private final ObservableList data = FXCollections.observableArrayList();
-
- public static class NameFormatterViewModel {
-
- private final SimpleStringProperty name;
- private final SimpleStringProperty format;
-
- NameFormatterViewModel() {
- this("");
- }
-
- NameFormatterViewModel(String name) {
- this(name, NameFormatter.DEFAULT_FORMAT);
- }
-
- NameFormatterViewModel(String name, String format) {
- this.name = new SimpleStringProperty(name);
- this.format = new SimpleStringProperty(format);
- }
-
- public String getName() {
- return name.get();
- }
-
- public void setName(String name) {
- this.name.set(name);
- }
-
- public String getFormat() {
- return format.get();
- }
-
- public void setFormat(String format) {
- this.format.set(format);
- }
- }
-
- /**
- * Tab to create custom Name Formatters
- *
- */
- public NameFormatterTab(JabRefPreferences prefs) {
- this.prefs = Objects.requireNonNull(prefs);
-
- ActionFactory factory = new ActionFactory(prefs.getKeyBindingRepository());
-
- TableColumn firstCol = new TableColumn<>(Localization.lang("Formatter name"));
- TableColumn lastCol = new TableColumn<>(Localization.lang("Format string"));
- table = new TableView<>();
- table.setEditable(true);
- firstCol.setCellValueFactory(new PropertyValueFactory<>("name"));
- firstCol.setCellFactory(TextFieldTableCell.forTableColumn());
- firstCol.setOnEditCommit(
- (TableColumn.CellEditEvent t) -> {
- t.getTableView().getItems().get(
- t.getTablePosition().getRow())
- .setName(t.getNewValue());
- });
- lastCol.setCellValueFactory(new PropertyValueFactory<>("format"));
- lastCol.setCellFactory(TextFieldTableCell.forTableColumn());
- lastCol.setOnEditCommit(
- (TableColumn.CellEditEvent t) -> {
- t.getTableView().getItems().get(
- t.getTablePosition().getRow())
- .setFormat(t.getNewValue());
- });
- firstCol.setPrefWidth(140);
- lastCol.setPrefWidth(200);
- table.setItems(data);
- table.getColumns().addAll(Arrays.asList(firstCol, lastCol));
- final TextField addName = new TextField();
- addName.setPromptText("name");
- addName.setMaxWidth(100);
- final TextField addLast = new TextField();
- addLast.setMaxWidth(100);
- addLast.setPromptText("format");
-
- BorderPane tabPanel = new BorderPane();
- ScrollPane scrollPane = new ScrollPane();
- scrollPane.setMaxHeight(400);
- scrollPane.setMaxWidth(360);
- scrollPane.setContent(table);
- tabPanel.setCenter(scrollPane);
-
- Label insertRows = new Label(Localization.lang("Insert rows"));
- insertRows.setVisible(false);
- Button add = new Button("Insert");
- add.setOnAction(e -> {
- if (!addName.getText().isEmpty() && !addLast.getText().isEmpty()) {
- NameFormatterViewModel tableRow = new NameFormatterViewModel(addName.getText(), addLast.getText());
- addName.clear();
- addLast.clear();
- data.add(tableRow);
- tableRows.add(tableRow);
- table.setItems(data);
- tableChanged = true;
- table.refresh();
- }
- });
- Label deleteRows = new Label(Localization.lang("Delete rows"));
- deleteRows.setVisible(false);
- Button delete = new Button("Delete");
- delete.setOnAction(e -> {
- if ((table.getFocusModel() != null) && (table.getFocusModel().getFocusedIndex() != -1)) {
- tableChanged = true;
- int row = table.getFocusModel().getFocusedIndex();
- NameFormatterViewModel tableRow = tableRows.get(row);
- tableRows.remove(tableRow);
- data.remove(tableRow);
- table.setItems(data);
- table.refresh();
- }
- });
-
- Button help = factory.createIconButton(StandardActions.HELP_NAME_FORMATTER, new HelpAction(HelpFile.CUSTOM_EXPORTS_NAME_FORMATTER));
- HBox toolbar = new HBox();
- toolbar.getChildren().addAll(addName, addLast, add, delete, help);
- tabPanel.setBottom(toolbar);
-
- Label specialNameFormatters = new Label(Localization.lang("Special name formatters"));
- specialNameFormatters.getStyleClass().add("sectionHeader");
- builder.add(specialNameFormatters, 1, 1);
- builder.add(tabPanel, 1, 2);
- }
-
- @Override
- public Node getBuilder() {
- return builder;
- }
-
- @Override
- public void setValues() {
- tableRows.clear();
- List names = prefs.getStringList(JabRefPreferences.NAME_FORMATER_KEY);
- List formats = prefs.getStringList(JabRefPreferences.NAME_FORMATTER_VALUE);
-
- for (int i = 0; i < names.size(); i++) {
- if (i < formats.size()) {
- tableRows.add(new NameFormatterViewModel(names.get(i), formats.get(i)));
- } else {
- tableRows.add(new NameFormatterViewModel(names.get(i)));
- }
- }
- }
-
- /**
- * Store changes to table preferences. This method is called when the user
- * clicks Ok.
- *
- */
- @Override
- public void storeSettings() {
-
- // Now we need to make sense of the contents the user has made to the
- // table setup table.
- if (tableChanged) {
- // First we remove all rows with empty names.
- int i = 0;
- while (i < tableRows.size()) {
- if (tableRows.get(i).getName().isEmpty()) {
- tableRows.remove(i);
- } else {
- i++;
- }
- }
- // Then we make lists
-
- List names = new ArrayList<>(tableRows.size());
- List formats = new ArrayList<>(tableRows.size());
-
- for (NameFormatterViewModel tr : tableRows) {
- names.add(tr.getName());
- formats.add(tr.getFormat());
- }
-
- // Finally, we store the new preferences.
- prefs.putStringList(JabRefPreferences.NAME_FORMATER_KEY, names);
- prefs.putStringList(JabRefPreferences.NAME_FORMATTER_VALUE, formats);
- }
- }
-
- @Override
- public boolean validateSettings() {
- return true;
- }
-
- @Override
- public String getTabName() {
- return Localization.lang("Name formatter");
- }
-
- @Override
- public List getRestartWarnings() { return new ArrayList<>(); }
-}
diff --git a/src/main/java/org/jabref/gui/preferences/NameFormatterTabView.java b/src/main/java/org/jabref/gui/preferences/NameFormatterTabView.java
new file mode 100644
index 00000000000..950c610d24e
--- /dev/null
+++ b/src/main/java/org/jabref/gui/preferences/NameFormatterTabView.java
@@ -0,0 +1,100 @@
+package org.jabref.gui.preferences;
+
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.TextField;
+import javafx.scene.control.cell.TextFieldTableCell;
+import javafx.scene.input.KeyCode;
+
+import org.jabref.Globals;
+import org.jabref.gui.actions.ActionFactory;
+import org.jabref.gui.actions.StandardActions;
+import org.jabref.gui.help.HelpAction;
+import org.jabref.gui.icon.IconTheme;
+import org.jabref.gui.util.ValueTableCellFactory;
+import org.jabref.logic.help.HelpFile;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.preferences.JabRefPreferences;
+
+import com.airhacks.afterburner.views.ViewLoader;
+
+public class NameFormatterTabView extends AbstractPreferenceTabView implements PreferencesTab {
+
+ @FXML private TableView formatterList;
+ @FXML private TableColumn formatterNameColumn;
+ @FXML private TableColumn formatterStringColumn;
+ @FXML private TableColumn actionsColumn;
+ @FXML private TextField addFormatterName;
+ @FXML private TextField addFormatterString;
+ @FXML private Button formatterHelp;
+
+ public NameFormatterTabView(JabRefPreferences preferences) {
+ this.preferences = preferences;
+
+ ViewLoader.view(this)
+ .root(this)
+ .load();
+ }
+
+ @Override
+ public String getTabName() { return Localization.lang("Name formatter"); }
+
+ public void initialize () {
+ NameFormatterTabViewModel nameFormatterTabViewModel = new NameFormatterTabViewModel(dialogService, preferences);
+ this.viewModel = nameFormatterTabViewModel;
+
+ formatterList.setEditable(true);
+
+ formatterNameColumn.setSortable(true);
+ formatterNameColumn.setReorderable(false);
+ formatterNameColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
+ formatterNameColumn.setCellFactory(TextFieldTableCell.forTableColumn());
+ formatterNameColumn.setEditable(true);
+ formatterNameColumn.setOnEditCommit(
+ (TableColumn.CellEditEvent event) -> {
+ event.getRowValue().setName(event.getNewValue());
+ });
+
+ formatterStringColumn.setSortable(true);
+ formatterStringColumn.setReorderable(false);
+ formatterStringColumn.setCellValueFactory(cellData -> cellData.getValue().formatProperty());
+ formatterStringColumn.setCellFactory(TextFieldTableCell.forTableColumn());
+ formatterStringColumn.setEditable(true);
+ formatterStringColumn.setOnEditCommit(
+ (TableColumn.CellEditEvent event) -> {
+ event.getRowValue().setFormat(event.getNewValue());
+ });
+
+ actionsColumn.setSortable(false);
+ actionsColumn.setReorderable(false);
+ actionsColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
+ new ValueTableCellFactory()
+ .withGraphic(name -> IconTheme.JabRefIcons.DELETE_ENTRY.getGraphicNode())
+ .withTooltip(name -> Localization.lang("Remove") + " " + name)
+ .withOnMouseClickedEvent(item -> evt -> {
+ nameFormatterTabViewModel.removeFormatter(formatterList.getFocusModel().getFocusedItem());
+ })
+ .install(actionsColumn);
+
+ formatterList.setOnKeyPressed(event -> {
+ if (event.getCode() == KeyCode.DELETE) {
+ nameFormatterTabViewModel.removeFormatter(formatterList.getSelectionModel().getSelectedItem());
+ }
+ });
+
+ formatterList.itemsProperty().bindBidirectional(nameFormatterTabViewModel.formatterListProperty());
+
+ addFormatterName.textProperty().bindBidirectional(nameFormatterTabViewModel.addFormatterNameProperty());
+ addFormatterString.textProperty().bindBidirectional(nameFormatterTabViewModel.addFormatterStringProperty());
+
+ ActionFactory actionFactory = new ActionFactory(Globals.getKeyPrefs());
+ actionFactory.configureIconButton(StandardActions.HELP_NAME_FORMATTER, new HelpAction(HelpFile.CUSTOM_EXPORTS_NAME_FORMATTER), formatterHelp);
+ }
+
+ public void addFormatter() {
+ ((NameFormatterTabViewModel) viewModel).addFormatter();
+ }
+
+}
diff --git a/src/main/java/org/jabref/gui/preferences/NameFormatterTabViewModel.java b/src/main/java/org/jabref/gui/preferences/NameFormatterTabViewModel.java
new file mode 100644
index 00000000000..2d510bcceb2
--- /dev/null
+++ b/src/main/java/org/jabref/gui/preferences/NameFormatterTabViewModel.java
@@ -0,0 +1,87 @@
+package org.jabref.gui.preferences;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javafx.beans.property.ListProperty;
+import javafx.beans.property.SimpleListProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+import javafx.collections.FXCollections;
+
+import org.jabref.gui.DialogService;
+import org.jabref.model.strings.StringUtil;
+import org.jabref.preferences.JabRefPreferences;
+
+public class NameFormatterTabViewModel implements PreferenceTabViewModel {
+
+ private final ListProperty formatterListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
+ private final StringProperty addFormatterNameProperty = new SimpleStringProperty();
+ private final StringProperty addFormatterStringProperty = new SimpleStringProperty();
+
+ private final DialogService dialogService;
+ private final JabRefPreferences preferences;
+
+ NameFormatterTabViewModel(DialogService dialogService, JabRefPreferences preferences) {
+ this.dialogService = dialogService;
+ this.preferences = preferences;
+ setValues();
+ }
+
+ @Override
+ public void setValues() {
+ formatterListProperty.clear();
+ List names = preferences.getStringList(JabRefPreferences.NAME_FORMATER_KEY);
+ List formats = preferences.getStringList(JabRefPreferences.NAME_FORMATTER_VALUE);
+
+ for (int i = 0; i < names.size(); i++) {
+ if (i < formats.size()) {
+ formatterListProperty.add(new NameFormatterItemModel(names.get(i), formats.get(i)));
+ } else {
+ formatterListProperty.add(new NameFormatterItemModel(names.get(i)));
+ }
+ }
+ }
+
+ @Override
+ public void storeSettings() {
+ formatterListProperty.removeIf(formatter -> formatter.getName().isEmpty());
+
+ List names = new ArrayList<>(formatterListProperty.size());
+ List formats = new ArrayList<>(formatterListProperty.size());
+ for (NameFormatterItemModel formatterListItem : formatterListProperty) {
+ names.add(formatterListItem.getName());
+ formats.add(formatterListItem.getFormat());
+ }
+
+ preferences.putStringList(JabRefPreferences.NAME_FORMATER_KEY, names);
+ preferences.putStringList(JabRefPreferences.NAME_FORMATTER_VALUE, formats);
+ }
+
+ public void addFormatter() {
+ if (!StringUtil.isNullOrEmpty(addFormatterNameProperty.getValue()) &&
+ !StringUtil.isNullOrEmpty(addFormatterStringProperty.getValue())) {
+
+ NameFormatterItemModel newFormatter = new NameFormatterItemModel(
+ addFormatterNameProperty.getValue(), addFormatterStringProperty.getValue());
+
+ addFormatterNameProperty.setValue("");
+ addFormatterStringProperty.setValue("");
+ formatterListProperty.add(newFormatter);
+ }
+ }
+
+ public void removeFormatter(NameFormatterItemModel formatter) { formatterListProperty.remove(formatter); }
+
+ @Override
+ public boolean validateSettings() { return true; }
+
+ @Override
+ public List getRestartWarnings() { return new ArrayList<>(); }
+
+ public ListProperty formatterListProperty() { return formatterListProperty; }
+
+ public StringProperty addFormatterNameProperty() { return addFormatterNameProperty; }
+
+ public StringProperty addFormatterStringProperty() { return addFormatterStringProperty; }
+}
diff --git a/src/main/java/org/jabref/gui/preferences/PreferencesDialogViewModel.java b/src/main/java/org/jabref/gui/preferences/PreferencesDialogViewModel.java
index 653a1e24cd8..d1a64d69c58 100644
--- a/src/main/java/org/jabref/gui/preferences/PreferencesDialogViewModel.java
+++ b/src/main/java/org/jabref/gui/preferences/PreferencesDialogViewModel.java
@@ -57,8 +57,8 @@ public PreferencesDialogViewModel(DialogService dialogService, TaskExecutor task
new BibtexKeyPatternPrefTab(prefs, frame.getCurrentBasePanel()),
new ImportSettingsTab(prefs),
new ExportSortingPrefsTab(prefs),
- new NameFormatterTab(prefs),
- new XmpPrefsTab(prefs),
+ new NameFormatterTabView(prefs),
+ new XmpPrivacyTabView(prefs),
new AdvancedTabView(prefs),
new AppearancePrefsTab(dialogService, prefs)
);
diff --git a/src/main/java/org/jabref/gui/preferences/XmpPrefsTab.java b/src/main/java/org/jabref/gui/preferences/XmpPrefsTab.java
deleted file mode 100644
index d0020403bb4..00000000000
--- a/src/main/java/org/jabref/gui/preferences/XmpPrefsTab.java
+++ /dev/null
@@ -1,181 +0,0 @@
-package org.jabref.gui.preferences;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.stream.Collectors;
-
-import javafx.beans.property.ListProperty;
-import javafx.beans.property.SimpleListProperty;
-import javafx.beans.property.SimpleStringProperty;
-import javafx.beans.property.StringProperty;
-import javafx.collections.FXCollections;
-import javafx.scene.Node;
-import javafx.scene.control.Button;
-import javafx.scene.control.CheckBox;
-import javafx.scene.control.ComboBox;
-import javafx.scene.control.Label;
-import javafx.scene.control.ScrollPane;
-import javafx.scene.control.TableColumn;
-import javafx.scene.control.TableColumn.CellEditEvent;
-import javafx.scene.control.TableView;
-import javafx.scene.layout.BorderPane;
-import javafx.scene.layout.GridPane;
-import javafx.scene.layout.HBox;
-import javafx.scene.layout.Pane;
-
-import org.jabref.gui.icon.IconTheme.JabRefIcons;
-import org.jabref.gui.util.ValueTableCellFactory;
-import org.jabref.logic.l10n.Localization;
-import org.jabref.model.entry.field.Field;
-import org.jabref.model.entry.field.FieldFactory;
-import org.jabref.model.strings.StringUtil;
-import org.jabref.preferences.JabRefPreferences;
-
-/**
- * Preference Tab for XMP.
- *
- * Allows the user to enable and configure the XMP privacy filter.
- */
-class XmpPrefsTab extends Pane implements PreferencesTab {
-
- private final JabRefPreferences prefs;
- private final GridPane builder = new GridPane();
- private final ListProperty fields = new SimpleListProperty<>(FXCollections.observableArrayList());
- private final CheckBox privacyFilterCheckBox = new CheckBox(
- Localization.lang("Do not write the following fields to XMP Metadata:"));
- private final TableView tableView = new TableView<>();
-
- /**
- * Customization of external program paths.
- */
- public XmpPrefsTab(JabRefPreferences prefs) {
- this.prefs = Objects.requireNonNull(prefs);
-
- tableView.itemsProperty().bindBidirectional(fields);
- TableColumn column = new TableColumn<>();
- column.setCellValueFactory(cellData -> cellData.getValue().field());
-
- TableColumn deleteIconColumn = new TableColumn<>();
- deleteIconColumn.setPrefWidth(60);
- deleteIconColumn.setCellValueFactory(cellData -> cellData.getValue().field());
- new ValueTableCellFactory()
- .withGraphic(item -> JabRefIcons.DELETE_ENTRY.getGraphicNode())
- .withOnMouseClickedEvent(item -> evt -> delete())
- .install(deleteIconColumn);
-
- column.setOnEditCommit((CellEditEvent cell) -> {
- cell.getRowValue().setField(cell.getNewValue());
- });
-
- tableView.getColumns().setAll(column, deleteIconColumn);
- tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
-
- ComboBox bibtexFields = new ComboBox<>(FXCollections.observableArrayList(FieldFactory.getCommonFields().stream().map(Field::getName).collect(Collectors.toSet())));
- bibtexFields.setEditable(true);
-
- BorderPane tablePanel = new BorderPane();
- ScrollPane scrollPane = new ScrollPane();
- scrollPane.setMaxHeight(400);
- scrollPane.setMaxWidth(400);
- scrollPane.setContent(tableView);
- tablePanel.setCenter(scrollPane);
-
- Button add = new Button("Add");
- add.setGraphic(JabRefIcons.ADD.getGraphicNode());
- add.setOnAction(e -> {
- if (!StringUtil.isNullOrEmpty(bibtexFields.getSelectionModel().getSelectedItem())) {
- XMPPrivacyFilter tableRow = new XMPPrivacyFilter(bibtexFields.getSelectionModel().getSelectedItem());
- fields.add(tableRow);
- }
- });
-
- HBox toolbar = new HBox(bibtexFields, add);
- tablePanel.setBottom(toolbar);
-
- // Build Prefs Tabs
- Label xmpExportPrivacySettings = new Label(Localization.lang("XMP export privacy settings"));
- xmpExportPrivacySettings.getStyleClass().add("sectionHeader");
- builder.add(xmpExportPrivacySettings, 1, 1);
- builder.add(privacyFilterCheckBox, 1, 2);
- builder.add(tablePanel, 1, 3);
-
- tableView.disableProperty().bind(privacyFilterCheckBox.selectedProperty().not());
- add.disableProperty().bind(privacyFilterCheckBox.selectedProperty().not());
- }
-
- private void delete() {
- if (tableView.getSelectionModel().getSelectedItem() != null) {
- XMPPrivacyFilter tableRow = tableView.getSelectionModel().getSelectedItem();
- fields.remove(tableRow);
- }
- }
-
- @Override
- public Node getBuilder() {
- return builder;
- }
-
- /**
- * Load settings from the preferences and initialize the table.
- */
- @Override
- public void setValues() {
- List xmpExclusions = prefs.getStringList(JabRefPreferences.XMP_PRIVACY_FILTERS).stream().map(XMPPrivacyFilter::new).collect(Collectors.toList());
- fields.setAll(xmpExclusions);
- privacyFilterCheckBox.setSelected(JabRefPreferences.getInstance().getBoolean(JabRefPreferences.USE_XMP_PRIVACY_FILTER));
- }
-
- /**
- * Store changes to table preferences. This method is called when the user
- * clicks Ok.
- *
- */
- @Override
- public void storeSettings() {
-
- fields.stream().filter(s -> StringUtil.isNullOrEmpty(s.getField())).forEach(fields::remove);
- prefs.putStringList(JabRefPreferences.XMP_PRIVACY_FILTERS,
- fields.stream().map(XMPPrivacyFilter::getField).collect(Collectors.toList()));
- prefs.putBoolean(JabRefPreferences.USE_XMP_PRIVACY_FILTER, privacyFilterCheckBox.isSelected());
- }
-
- @Override
- public boolean validateSettings() {
- return true;
- }
-
- @Override
- public String getTabName() {
- return Localization.lang("XMP-metadata");
- }
-
- @Override
- public List getRestartWarnings() { return new ArrayList<>(); }
-
- private class XMPPrivacyFilter {
-
- private final SimpleStringProperty field;
-
- XMPPrivacyFilter(String field) {
- this.field = new SimpleStringProperty(field);
- }
-
- public void setField(String field) {
- this.field.set(field);
- }
-
- public String getField() {
- return field.get();
- }
-
- public StringProperty field() {
- return field;
- }
-
- @Override
- public String toString() {
- return field.getValue();
- }
- }
-}
diff --git a/src/main/java/org/jabref/gui/preferences/XmpPrivacyTab.fxml b/src/main/java/org/jabref/gui/preferences/XmpPrivacyTab.fxml
new file mode 100644
index 00000000000..d9684450cc5
--- /dev/null
+++ b/src/main/java/org/jabref/gui/preferences/XmpPrivacyTab.fxml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/org/jabref/gui/preferences/XmpPrivacyTabView.java b/src/main/java/org/jabref/gui/preferences/XmpPrivacyTabView.java
new file mode 100644
index 00000000000..a977be8ccf3
--- /dev/null
+++ b/src/main/java/org/jabref/gui/preferences/XmpPrivacyTabView.java
@@ -0,0 +1,111 @@
+package org.jabref.gui.preferences;
+
+import javafx.application.Platform;
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.ComboBox;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.input.KeyCode;
+import javafx.util.StringConverter;
+
+import org.jabref.gui.icon.IconTheme;
+import org.jabref.gui.util.BindingsHelper;
+import org.jabref.gui.util.FieldsUtil;
+import org.jabref.gui.util.IconValidationDecorator;
+import org.jabref.gui.util.ValueTableCellFactory;
+import org.jabref.gui.util.ViewModelListCellFactory;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.model.entry.field.Field;
+import org.jabref.model.entry.field.FieldFactory;
+import org.jabref.preferences.JabRefPreferences;
+
+import com.airhacks.afterburner.views.ViewLoader;
+import de.saxsys.mvvmfx.utils.validation.visualization.ControlsFxVisualizer;
+
+public class XmpPrivacyTabView extends AbstractPreferenceTabView implements PreferencesTab {
+
+ @FXML private CheckBox enableXmpFilter;
+ @FXML private TableView filterList;
+ @FXML private TableColumn fieldColumn;
+ @FXML private TableColumn actionsColumn;
+ @FXML private ComboBox addFieldName;
+ @FXML private Button addField;
+
+ private ControlsFxVisualizer validationVisualizer = new ControlsFxVisualizer();
+
+ public XmpPrivacyTabView(JabRefPreferences preferences) {
+ this.preferences = preferences;
+
+ ViewLoader.view(this)
+ .root(this)
+ .load();
+ }
+
+ @Override
+ public String getTabName() { return Localization.lang("XMP-metadata"); }
+
+ public void initialize () {
+ XmpPrivacyTabViewModel xmpPrivacyTabViewModel = new XmpPrivacyTabViewModel(dialogService, preferences);
+ this.viewModel = xmpPrivacyTabViewModel;
+
+ enableXmpFilter.selectedProperty().bindBidirectional(xmpPrivacyTabViewModel.xmpFilterEnabledProperty());
+ filterList.disableProperty().bind(xmpPrivacyTabViewModel.xmpFilterEnabledProperty().not());
+ addFieldName.disableProperty().bind(xmpPrivacyTabViewModel.xmpFilterEnabledProperty().not());
+ addField.disableProperty().bind(xmpPrivacyTabViewModel.xmpFilterEnabledProperty().not());
+
+ fieldColumn.setSortable(true);
+ fieldColumn.setReorderable(false);
+ fieldColumn.setCellValueFactory(cellData -> BindingsHelper.constantOf(cellData.getValue()));
+ new ValueTableCellFactory()
+ .withText(FieldsUtil::getNameWithType)
+ .install(fieldColumn);
+
+ actionsColumn.setSortable(false);
+ actionsColumn.setReorderable(false);
+ actionsColumn.setCellValueFactory(cellData -> BindingsHelper.constantOf(cellData.getValue()));
+ new ValueTableCellFactory()
+ .withGraphic(item -> IconTheme.JabRefIcons.DELETE_ENTRY.getGraphicNode())
+ .withTooltip(item -> Localization.lang("Remove") + " " + item.getName())
+ .withOnMouseClickedEvent(item -> evt -> {
+ xmpPrivacyTabViewModel.removeFilter(filterList.getFocusModel().getFocusedItem());
+ })
+ .install(actionsColumn);
+
+ filterList.setOnKeyPressed(event -> {
+ if (event.getCode() == KeyCode.DELETE) {
+ xmpPrivacyTabViewModel.removeFilter(filterList.getSelectionModel().getSelectedItem());
+ }
+ });
+
+ filterList.itemsProperty().bind(xmpPrivacyTabViewModel.filterListProperty());
+
+ addFieldName.setEditable(true);
+ new ViewModelListCellFactory()
+ .withText(FieldsUtil::getNameWithType)
+ .install(addFieldName);
+ addFieldName.itemsProperty().bind(xmpPrivacyTabViewModel.availableFieldsProperty());
+ addFieldName.valueProperty().bindBidirectional(xmpPrivacyTabViewModel.addFieldNameProperty());
+ addFieldName.setConverter(new StringConverter<>() {
+ @Override
+ public String toString(Field object) {
+ if (object != null) {
+ return object.getDisplayName();
+ } else {
+ return "";
+ }
+ }
+
+ @Override
+ public Field fromString(String string) {
+ return FieldFactory.parseField(string);
+ }
+ });
+
+ validationVisualizer.setDecoration(new IconValidationDecorator());
+ Platform.runLater(() -> validationVisualizer.initVisualization(xmpPrivacyTabViewModel.xmpFilterListValidationStatus(), filterList));
+ }
+
+ public void addField() { ((XmpPrivacyTabViewModel) viewModel).addField(); }
+}
diff --git a/src/main/java/org/jabref/gui/preferences/XmpPrivacyTabViewModel.java b/src/main/java/org/jabref/gui/preferences/XmpPrivacyTabViewModel.java
new file mode 100644
index 00000000000..ad9d5b7445f
--- /dev/null
+++ b/src/main/java/org/jabref/gui/preferences/XmpPrivacyTabViewModel.java
@@ -0,0 +1,114 @@
+package org.jabref.gui.preferences;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.ListProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.SimpleListProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.collections.FXCollections;
+
+import org.jabref.gui.DialogService;
+import org.jabref.logic.l10n.Localization;
+import org.jabref.model.entry.field.Field;
+import org.jabref.model.entry.field.FieldFactory;
+import org.jabref.preferences.JabRefPreferences;
+
+import de.saxsys.mvvmfx.utils.validation.FunctionBasedValidator;
+import de.saxsys.mvvmfx.utils.validation.ValidationMessage;
+import de.saxsys.mvvmfx.utils.validation.ValidationStatus;
+
+public class XmpPrivacyTabViewModel implements PreferenceTabViewModel {
+
+ private final BooleanProperty xmpFilterEnabledProperty = new SimpleBooleanProperty();
+ private final ListProperty xmpFilterListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
+ private final ListProperty availableFieldsProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
+ private final ObjectProperty addFieldProperty = new SimpleObjectProperty<>();
+
+ private final DialogService dialogService;
+ private final JabRefPreferences preferences;
+
+ private FunctionBasedValidator xmpFilterListValidator;
+
+ XmpPrivacyTabViewModel(DialogService dialogService, JabRefPreferences preferences) {
+ this.dialogService = dialogService;
+ this.preferences = preferences;
+
+ xmpFilterListValidator = new FunctionBasedValidator<>(
+ xmpFilterListProperty,
+ input -> input.size() > 0,
+ ValidationMessage.error(String.format("%s > %s %n %n %s",
+ Localization.lang("xmp-metadata"),
+ Localization.lang("Filter List"),
+ Localization.lang("List must not be empty."))));
+
+ setValues();
+ }
+
+ @Override
+ public void setValues() {
+ xmpFilterEnabledProperty.setValue(preferences.getBoolean(JabRefPreferences.USE_XMP_PRIVACY_FILTER));
+
+ xmpFilterListProperty.clear();
+ List xmpFilters = preferences.getStringList(JabRefPreferences.XMP_PRIVACY_FILTERS)
+ .stream().map(FieldFactory::parseField).collect(Collectors.toList());
+ xmpFilterListProperty.addAll(xmpFilters);
+
+ availableFieldsProperty.clear();
+ availableFieldsProperty.addAll(FieldFactory.getCommonFields());
+ }
+
+ @Override
+ public void storeSettings() {
+ preferences.putBoolean(JabRefPreferences.USE_XMP_PRIVACY_FILTER, xmpFilterEnabledProperty.getValue());
+ preferences.putStringList(JabRefPreferences.XMP_PRIVACY_FILTERS, xmpFilterListProperty.getValue().stream()
+ .map(Field::getName)
+ .collect(Collectors.toList()));
+ }
+
+ public void addField() {
+ if (addFieldProperty.getValue() == null) {
+ return;
+ }
+
+ if (xmpFilterListProperty.getValue().stream().filter(item -> item.equals(addFieldProperty.getValue())).findAny().isEmpty()) {
+ xmpFilterListProperty.add(addFieldProperty.getValue());
+ addFieldProperty.setValue(null);
+ }
+ }
+
+ public void removeFilter(Field filter) {
+ xmpFilterListProperty.remove(filter);
+ }
+
+ public ValidationStatus xmpFilterListValidationStatus() {
+ return xmpFilterListValidator.getValidationStatus();
+ }
+
+ @Override
+ public boolean validateSettings() {
+ ValidationStatus validationStatus = xmpFilterListValidationStatus();
+ if (xmpFilterEnabledProperty.getValue() && !validationStatus.isValid()) {
+ validationStatus.getHighestMessage().ifPresent(message ->
+ dialogService.showErrorDialogAndWait(message.getMessage()));
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public List getRestartWarnings() { return new ArrayList<>(); }
+
+ public BooleanProperty xmpFilterEnabledProperty() { return xmpFilterEnabledProperty; }
+
+ public ListProperty filterListProperty() { return xmpFilterListProperty; }
+
+ public ListProperty availableFieldsProperty() { return availableFieldsProperty; }
+
+ public ObjectProperty addFieldNameProperty() { return addFieldProperty; }
+
+}
diff --git a/src/main/java/org/jabref/gui/util/FieldsUtil.java b/src/main/java/org/jabref/gui/util/FieldsUtil.java
new file mode 100644
index 00000000000..e313a064481
--- /dev/null
+++ b/src/main/java/org/jabref/gui/util/FieldsUtil.java
@@ -0,0 +1,55 @@
+package org.jabref.gui.util;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.jabref.logic.l10n.Localization;
+import org.jabref.model.entry.field.Field;
+import org.jabref.model.entry.field.FieldProperty;
+import org.jabref.model.entry.field.IEEEField;
+import org.jabref.model.entry.field.InternalField;
+import org.jabref.model.entry.field.SpecialField;
+import org.jabref.model.entry.field.UnknownField;
+
+public class FieldsUtil {
+
+ public static String getNameWithType(Field field) {
+ if (field instanceof SpecialField) {
+ return field.getDisplayName() + " (" + Localization.lang("Special") + ")";
+ } else if (field instanceof IEEEField) {
+ return field.getDisplayName() + " (" + Localization.lang("IEEE") + ")";
+ } else if (field instanceof InternalField) {
+ return field.getDisplayName() + " (" + Localization.lang("Internal") + ")";
+ } else if (field instanceof UnknownField) {
+ return field.getDisplayName() + " (" + Localization.lang("Custom") + ")";
+ } else if (field instanceof ExtraFilePseudoField) {
+ return field.getDisplayName() + " (" + Localization.lang("File type") + ")";
+ } else {
+ return field.getDisplayName();
+ }
+ }
+
+ public static class ExtraFilePseudoField implements Field {
+
+ String name;
+
+ public ExtraFilePseudoField(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public Set getProperties() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isStandardField() {
+ return false;
+ }
+ }
+}
diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties
index d33984a30b1..639970999d2 100644
--- a/src/main/resources/l10n/JabRef_en.properties
+++ b/src/main/resources/l10n/JabRef_en.properties
@@ -234,8 +234,6 @@ delete\ entry=delete entry
Delete\ multiple\ entries=Delete multiple entries
-Delete\ rows=Delete rows
-
Deleted=Deleted
Permanently\ delete\ local\ file=Permanently delete local file
@@ -264,7 +262,7 @@ Do\ not\ open\ any\ files\ at\ startup=Do not open any files at startup
Do\ not\ overwrite\ existing\ keys=Do not overwrite existing keys
Do\ not\ wrap\ the\ following\ fields\ when\ saving=Do not wrap the following fields when saving
-Do\ not\ write\ the\ following\ fields\ to\ XMP\ Metadata\:=Do not write the following fields to XMP Metadata:
+Do\ not\ write\ the\ following\ fields\ to\ XMP\ Metadata=Do not write the following fields to XMP Metadata
Donate\ to\ JabRef=Donate to JabRef
@@ -483,7 +481,6 @@ Include\ subgroups\:\ When\ selected,\ view\ entries\ contained\ in\ this\ group
Independent\ group\:\ When\ selected,\ view\ only\ this\ group's\ entries=Independent group: When selected, view only this group's entries
I\ Agree=I Agree
-Insert\ rows=Insert rows
Invalid\ BibTeX\ key=Invalid BibTeX key
@@ -563,6 +560,7 @@ No\ recommendations\ received\ from\ Mr.\ DLib\ for\ this\ entry.=No recommendat
Error\ while\ fetching\ recommendations\ from\ Mr.DLib.=Error while fetching recommendations from Mr.DLib.
Name=Name
+
Name\ formatter=Name formatter
Natbib\ style=Natbib style
@@ -2114,3 +2112,13 @@ Set\ LaTeX\ file\ directory=Set LaTeX file directory
Import\ entries\ from\ LaTeX\ files=Import entries from LaTeX files
Import\ new\ entries=Import new entries
Group\ color=Group color
+
+Add\ field\ to\ filter\ list=Add field to filter list
+Add\ formatter\ to\ list=Add formatter to list
+IEEE=IEEE
+Internal=Internal
+Special=Special
+File\ type=File type
+Filter\ List=Filter List
+List\ must\ not\ be\ empty.=List must not be empty.
+xmp-metadata=xmp-metadata