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