diff --git a/src/main/java/org/jabref/gui/customentrytypes/CustomEntryTypeDialogViewModel.java b/src/main/java/org/jabref/gui/customentrytypes/CustomEntryTypeDialogViewModel.java index 0dddfbead21..8ce62756fb7 100644 --- a/src/main/java/org/jabref/gui/customentrytypes/CustomEntryTypeDialogViewModel.java +++ b/src/main/java/org/jabref/gui/customentrytypes/CustomEntryTypeDialogViewModel.java @@ -1,57 +1,189 @@ package org.jabref.gui.customentrytypes; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import javafx.beans.Observable; +import javafx.beans.property.ListProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleListProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.util.StringConverter; + +import org.jabref.Globals; +import org.jabref.model.database.BibDatabaseMode; +import org.jabref.model.entry.BibEntryType; +import org.jabref.model.entry.field.BibField; import org.jabref.model.entry.field.Field; +import org.jabref.model.entry.field.FieldFactory; +import org.jabref.model.entry.field.FieldPriority; +import org.jabref.model.entry.field.OrFields; +import org.jabref.model.entry.field.UnknownField; import org.jabref.model.entry.types.EntryType; +import org.jabref.model.entry.types.UnknownEntryType; +import org.jabref.preferences.PreferencesService; + +import org.fxmisc.easybind.EasyBind; public class CustomEntryTypeDialogViewModel { - public CustomEntryTypeDialogViewModel() { + public static final StringConverter FIELD_STRING_CONVERTER = new StringConverter<>() { - } + @Override + public String toString(Field object) { + return object != null ? object.getDisplayName() : ""; + } + + @Override + public Field fromString(String string) { + return new UnknownField(string); + } + }; + + private ListProperty entryTypesProperty; + private ListProperty fieldsProperty; + private ObjectProperty selectedEntryTypesProperty = new SimpleObjectProperty<>(); + private ListProperty fieldsForTypeProperty; + private ObjectProperty selectedFieldToAddProperty = new SimpleObjectProperty<>(); + private StringProperty entryTypeToAddProperty = new SimpleStringProperty(""); + private ObservableList entryTypes; + private ObservableList fieldsForType = FXCollections.observableArrayList(extractor -> new Observable[] {extractor.fieldNameProperty(), extractor.fieldTypeProperty()}); + private ObjectProperty newFieldToAddProperty = new SimpleObjectProperty<>(); + private BibDatabaseMode mode; + private Map> typesWithFields = new HashMap<>(); + private List typesToRemove = new ArrayList<>(); + + private PreferencesService preferencesService; + + public CustomEntryTypeDialogViewModel(BibDatabaseMode mode, PreferencesService preferencesService) { + this.mode = mode; + this.preferencesService = preferencesService; + + Collection allTypes = Globals.entryTypesManager.getAllTypes(mode); + allTypes.addAll(Globals.entryTypesManager.getAllCustomTypes(mode)); + + entryTypes = FXCollections.observableArrayList(allTypes); + entryTypesProperty = new SimpleListProperty<>(entryTypes); - public void addNewOptionalField2() { - // TODO Auto-generated method stub + fieldsProperty = new SimpleListProperty<>(FXCollections.observableArrayList(FieldFactory.getAllFields())); + + for (BibEntryType entryType : allTypes) { + List fields = entryType.getAllFields().stream().map(bibField -> new FieldViewModel(bibField.getField(), entryType.isRequired(bibField.getField()), bibField.getPriority(), entryType)).collect(Collectors.toList()); + typesWithFields.put(entryType, fields); + } + + this.fieldsForTypeProperty = new SimpleListProperty<>(fieldsForType); + + EasyBind.subscribe(selectedEntryTypesProperty, type -> { + if (type != null) { + List typesForField = typesWithFields.get(type); + fieldsForType.setAll(typesForField); + } + }); } - public void addNewOptionalField() { - // TODO Auto-generated method stub + public ListProperty entryTypesProperty() { + return this.entryTypesProperty; + } + public ListProperty fieldsProperty() { + return this.fieldsProperty; } - public void addNewRequiredField() { - // TODO Auto-generated method stub + public enum FieldType { + + REQUIRED("Required"), + OPTIONAL("Optional"); + + private String name; + + FieldType(String name) { + this.name = name; + } + public String getDisplayName() { + return this.name; + } + + @Override + public String toString() { + return this.name; + } + } + + public void addNewField() { + Field field = newFieldToAddProperty.getValue(); + FieldViewModel model = new FieldViewModel(field, true, FieldPriority.IMPORTANT, selectedEntryTypesProperty.getValue()); + typesWithFields.computeIfAbsent(selectedEntryTypesProperty.getValue(), key -> new ArrayList<>()).add(model); + fieldsForType.add(model); } public void addNewCustomEntryType() { - // TODO Auto-generated method stub + EntryType newentryType = new UnknownEntryType(entryTypeToAddProperty.getValue()); + BibEntryType type = new BibEntryType(newentryType, new ArrayList<>(), Collections.emptyList()); + this.entryTypes.add(type); + this.typesWithFields.put(type, new ArrayList<>()); } - public Object removeEntryType(EntryType focusedItem) { - // TODO Auto-generated method stub - return null; + public ObjectProperty selectedEntryTypeProperty() { + return this.selectedEntryTypesProperty; } - public Object removeRequiredField(Field focusedItem) { - // TODO Auto-generated method stub - return null; + public ListProperty fieldsforTypesProperty() { + return this.fieldsForTypeProperty; } - public Object removeOptionalField(Field focusedItem) { - // TODO Auto-generated method stub - return null; + public ObjectProperty selectedFieldToAddProperty() { + return this.selectedFieldToAddProperty; } - public Object removeOptional2Field(Field focusedItem) { - // TODO Auto-generated method stub - return null; + public StringProperty entryTypeToAddProperty() { + return this.entryTypeToAddProperty; } - public enum FieldType { - REQUIRED, - PRIMARY_OPTIONAL, - SECONDARY_OPTIONAL + public ObjectProperty newFieldToAddProperty() { + return this.newFieldToAddProperty; + } + + public void removeEntryType(BibEntryType focusedItem) { + typesToRemove.add(focusedItem); + typesWithFields.remove(focusedItem); + entryTypes.remove(focusedItem); + } + + public void removeField(FieldViewModel focusedItem) { + typesWithFields.computeIfAbsent(selectedEntryTypesProperty.getValue(), key -> new ArrayList<>()).remove(focusedItem); + fieldsForType.remove(focusedItem); } + + public void apply() { + + for (var entry : typesWithFields.entrySet()) { + BibEntryType type = entry.getKey(); + List allFields = entry.getValue(); + + List requiredFields = allFields.stream().filter(field -> field.getFieldType() == FieldType.REQUIRED).map(FieldViewModel::getField).map(OrFields::new).collect(Collectors.toList()); + List otherFields = allFields.stream().filter(field -> field.getFieldType() == FieldType.OPTIONAL).map(bibField -> new BibField(bibField.getField(), bibField.getFieldPriority())).collect(Collectors.toList()); + + BibEntryType newType = new BibEntryType(type.getType(), otherFields, requiredFields); + Globals.entryTypesManager.addCustomOrModifiedType(newType, mode); + } + + for (var type : typesToRemove) { + Globals.entryTypesManager.removeCustomEntryType(type, mode); + } + preferencesService.saveCustomEntryTypes(); + } + } diff --git a/src/main/java/org/jabref/gui/customentrytypes/CustomizeEntryTypeDialog.fxml b/src/main/java/org/jabref/gui/customentrytypes/CustomizeEntryTypeDialog.fxml index 39b9892d363..6ed035d2754 100644 --- a/src/main/java/org/jabref/gui/customentrytypes/CustomizeEntryTypeDialog.fxml +++ b/src/main/java/org/jabref/gui/customentrytypes/CustomizeEntryTypeDialog.fxml @@ -1,5 +1,6 @@ + @@ -7,51 +8,94 @@ + - - - - - - - + + + + + + + + + + diff --git a/src/main/java/org/jabref/gui/customentrytypes/CustomizeEntryTypeDialogView.java b/src/main/java/org/jabref/gui/customentrytypes/CustomizeEntryTypeDialogView.java index fd02326b7e2..0cd7fc03b0f 100644 --- a/src/main/java/org/jabref/gui/customentrytypes/CustomizeEntryTypeDialogView.java +++ b/src/main/java/org/jabref/gui/customentrytypes/CustomizeEntryTypeDialogView.java @@ -1,51 +1,108 @@ package org.jabref.gui.customentrytypes; +import java.util.EnumSet; + +import javax.inject.Inject; + import javafx.beans.property.ReadOnlyStringWrapper; import javafx.fxml.FXML; +import javafx.scene.control.ButtonBar.ButtonData; import javafx.scene.control.ButtonType; import javafx.scene.control.ComboBox; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; +import javafx.scene.control.TextField; +import org.jabref.gui.customentrytypes.CustomEntryTypeDialogViewModel.FieldType; +import org.jabref.gui.icon.IconTheme; import org.jabref.gui.util.BaseDialog; +import org.jabref.gui.util.ValueTableCellFactory; +import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabaseContext; +import org.jabref.model.database.BibDatabaseMode; +import org.jabref.model.entry.BibEntryType; import org.jabref.model.entry.field.Field; -import org.jabref.model.entry.types.EntryType; +import org.jabref.preferences.PreferencesService; import com.airhacks.afterburner.views.ViewLoader; public class CustomizeEntryTypeDialogView extends BaseDialog { - @FXML private TableView entryTypes; - @FXML private TableColumn entryTypColumn; - @FXML private TableColumn entryTypeActionsColumn; - @FXML private ComboBox addNewEntryType; - @FXML private TableView requiredFields; - @FXML private TableColumn requiredFieldsNameColumn; - @FXML private TableColumn fieldTypeColumn; - @FXML private TableColumn fieldTypeActionColumn; - @FXML private ComboBox addNewField; + @FXML private TableView entryTypes; + @FXML private TableColumn entryTypColumn; + @FXML private TableColumn entryTypeActionsColumn; + @FXML private TextField addNewEntryType; + @FXML private TableView fields; + @FXML private TableColumn fieldNameColumn; + @FXML private TableColumn fieldTypeColumn; + @FXML private TableColumn fieldTypeActionColumn; + @FXML private ComboBox addNewField; @FXML private ButtonType applyButton; - private final CustomEntryTypeDialogViewModel viewModel; + @Inject private PreferencesService preferencesService; - public CustomizeEntryTypeDialogView(BibDatabaseContext bibDatabaseContext) { + private CustomEntryTypeDialogViewModel viewModel; + private BibDatabaseMode mode; - viewModel = new CustomEntryTypeDialogViewModel(); + public CustomizeEntryTypeDialogView(BibDatabaseContext bibDatabaseContext) { + this.mode = bibDatabaseContext.getMode(); ViewLoader.view(this) .load() .setAsDialogPane(this); + + setResultConverter(button -> { + if (button.getButtonData() == ButtonData.OK_DONE) { + viewModel.apply(); + } + return null; + }); } @FXML private void initialize() { + viewModel = new CustomEntryTypeDialogViewModel(mode, preferencesService); + setupTable(); } private void setupTable() { - entryTypColumn.setCellValueFactory(cellData -> new ReadOnlyStringWrapper(cellData.getValue().getDisplayName())); + entryTypColumn.setCellValueFactory(cellData -> new ReadOnlyStringWrapper(cellData.getValue().getType().getDisplayName())); + entryTypes.itemsProperty().bind(viewModel.entryTypesProperty()); + entryTypes.getSelectionModel().selectFirst(); + + entryTypeActionsColumn.setSortable(false); + entryTypeActionsColumn.setReorderable(false); + entryTypeActionsColumn.setCellValueFactory(cellData -> new ReadOnlyStringWrapper(cellData.getValue().getType().getDisplayName())); + new ValueTableCellFactory() + .withGraphic(item -> IconTheme.JabRefIcons.DELETE_ENTRY.getGraphicNode()) + .withTooltip(name -> Localization.lang("Remove entry type") + " " + name) + .withOnMouseClickedEvent(item -> evt -> viewModel.removeEntryType(entryTypes.getFocusModel().getFocusedItem())) + .install(entryTypeActionsColumn); + + fieldTypeColumn.setCellFactory(cellData -> new RadioButtonCell<>(EnumSet.allOf(FieldType.class))); + fieldTypeColumn.setCellValueFactory(item -> item.getValue().fieldTypeProperty()); + fieldNameColumn.setCellValueFactory(item -> item.getValue().fieldNameProperty()); + + viewModel.selectedEntryTypeProperty().bind(entryTypes.getSelectionModel().selectedItemProperty()); + viewModel.entryTypeToAddProperty().bind(addNewEntryType.textProperty()); + + addNewField.setItems(viewModel.fieldsProperty()); + addNewField.setConverter(viewModel.FIELD_STRING_CONVERTER); + + fieldTypeActionColumn.setSortable(false); + fieldTypeActionColumn.setReorderable(false); + fieldTypeActionColumn.setCellValueFactory(cellData -> cellData.getValue().fieldNameProperty()); + + new ValueTableCellFactory() + .withGraphic(item -> IconTheme.JabRefIcons.DELETE_ENTRY.getGraphicNode()) + .withTooltip(name -> Localization.lang("Remove field from entry type") + " " + name) + .withOnMouseClickedEvent(item -> evt -> viewModel.removeField(fields.getFocusModel().getFocusedItem())) + .install(fieldTypeActionColumn); + + viewModel.newFieldToAddProperty().bind(addNewField.valueProperty()); + fields.itemsProperty().bindBidirectional(viewModel.fieldsforTypesProperty()); } @FXML @@ -54,21 +111,8 @@ void addEntryType() { } @FXML - void addRequiredFields() { - viewModel.addNewRequiredField(); - - } - - @FXML - void addOptionalField() { - viewModel.addNewOptionalField(); - - } - - @FXML - void addOptionalField2() { - viewModel.addNewOptionalField2(); - + void addNewField() { + viewModel.addNewField(); } } diff --git a/src/main/java/org/jabref/gui/customentrytypes/FieldViewModel.java b/src/main/java/org/jabref/gui/customentrytypes/FieldViewModel.java new file mode 100644 index 00000000000..7cd59136b0b --- /dev/null +++ b/src/main/java/org/jabref/gui/customentrytypes/FieldViewModel.java @@ -0,0 +1,61 @@ +package org.jabref.gui.customentrytypes; + +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; + +import org.jabref.gui.customentrytypes.CustomEntryTypeDialogViewModel.FieldType; +import org.jabref.model.entry.BibEntryType; +import org.jabref.model.entry.field.Field; +import org.jabref.model.entry.field.FieldPriority; + +public class FieldViewModel { + + private final ObjectProperty fieldTypeProperty = new SimpleObjectProperty<>(); + private final StringProperty fieldNameProperty = new SimpleStringProperty(""); + private final Field field; + private final FieldPriority fieldPriority; + private BibEntryType entryType; + + public FieldViewModel(Field field, FieldType fieldType, FieldPriority fieldPriority, BibEntryType entryType) { + this.field = field; + this.entryType = entryType; + this.fieldNameProperty.setValue(field.getDisplayName()); + this.fieldTypeProperty.setValue(fieldType); + this.fieldPriority = fieldPriority; + } + + public FieldViewModel(Field field, boolean required, FieldPriority fieldPriority, BibEntryType entryType) { + this(field, required ? FieldType.REQUIRED : FieldType.OPTIONAL, fieldPriority, entryType); + } + + public ObjectProperty fieldTypeProperty() { + return this.fieldTypeProperty; + } + + public StringProperty fieldNameProperty() { + return this.fieldNameProperty; + } + + public Field getField() { + return this.field; + } + + public BibEntryType getEntryType() { + return this.entryType; + } + + public FieldPriority getFieldPriority() { + return this.fieldPriority; + } + + public FieldType getFieldType() { + return this.fieldTypeProperty.getValue(); + } + + @Override + public String toString() { + return this.field.getDisplayName(); + } +} diff --git a/src/main/java/org/jabref/gui/customentrytypes/RadioButtonCell.java b/src/main/java/org/jabref/gui/customentrytypes/RadioButtonCell.java index cd1b15a51bd..ec3bfdc890e 100644 --- a/src/main/java/org/jabref/gui/customentrytypes/RadioButtonCell.java +++ b/src/main/java/org/jabref/gui/customentrytypes/RadioButtonCell.java @@ -10,6 +10,7 @@ import javafx.scene.control.Toggle; import javafx.scene.control.ToggleGroup; import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; public class RadioButtonCell> extends TableCell { @@ -22,7 +23,9 @@ public RadioButtonCell(EnumSet enumeration) { @Override protected void updateItem(T item, boolean empty) { super.updateItem(item, empty); - if (!empty) { + if (empty || (item == null)) { + setGraphic(null); + } else { // gui setup HBox hb = new HBox(7); hb.setAlignment(Pos.CENTER); @@ -37,6 +40,7 @@ protected void updateItem(T item, boolean empty) { if (enumElement.equals(item)) { radioButton.setSelected(true); } + hb.setHgrow(radioButton, Priority.ALWAYS); } // issue events on change of the selected radio button diff --git a/src/main/java/org/jabref/gui/logging/plugins/Log4jPlugins.java b/src/main/java/org/jabref/gui/logging/plugins/Log4jPlugins.java deleted file mode 100644 index d43e19ed567..00000000000 --- a/src/main/java/org/jabref/gui/logging/plugins/Log4jPlugins.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.jabref.gui.logging.plugins; - -import org.apache.logging.log4j.plugins.processor.PluginEntry; -import org.apache.logging.log4j.plugins.processor.PluginService; - -public class Log4jPlugins extends PluginService { - - private static PluginEntry[] entries = new PluginEntry[] { - new PluginEntry("ourapplicationinsightsappender", "org.jabref.gui.logging.ApplicationInsightsAppender", "appender", true, false, "Core"), - new PluginEntry("guiappender", "org.jabref.gui.logging.GuiAppender", "appender", true, false, "Core") - }; - @Override - public PluginEntry[] getEntries() { return entries;} -} diff --git a/src/main/java/org/jabref/model/entry/BibEntryTypesManager.java b/src/main/java/org/jabref/model/entry/BibEntryTypesManager.java index f278b8d5a50..81bd2d75ef3 100644 --- a/src/main/java/org/jabref/model/entry/BibEntryTypesManager.java +++ b/src/main/java/org/jabref/model/entry/BibEntryTypesManager.java @@ -112,6 +112,14 @@ public void addCustomOrModifiedType(BibEntryType entryType, BibDatabaseMode mode } } + public void removeCustomEntryType(BibEntryType entryType, BibDatabaseMode mode) { + if (BibDatabaseMode.BIBLATEX == mode) { + BIBLATEX.removeCustomEntryType(entryType); + } else if (BibDatabaseMode.BIBTEX == mode) { + BIBTEX.removeCustomEntryType(entryType); + } + } + public Collection getAllTypes(BibDatabaseMode type) { return type == BibDatabaseMode.BIBLATEX ? BIBLATEX.getAllTypes() : BIBTEX.getAllTypes(); } @@ -170,6 +178,10 @@ private void addCustomOrModifiedType(BibEntryType type) { customOrModifiedType.remove(type); customOrModifiedType.add(type); } + + private void removeCustomEntryType(BibEntryType type) { + customOrModifiedType.remove(type); + } public SortedSet getAllTypes() { SortedSet allTypes = new TreeSet<>(customOrModifiedType); diff --git a/src/main/java/org/jabref/model/entry/field/FieldFactory.java b/src/main/java/org/jabref/model/entry/field/FieldFactory.java index a5086283f6a..7372ffc1345 100644 --- a/src/main/java/org/jabref/model/entry/field/FieldFactory.java +++ b/src/main/java/org/jabref/model/entry/field/FieldFactory.java @@ -118,7 +118,7 @@ private static Set getFieldsFiltered(Predicate selector) { .collect(Collectors.toSet()); } - private static Set getAllFields() { + public static Set getAllFields() { Set fields = new HashSet<>(); fields.addAll(EnumSet.allOf(IEEEField.class)); fields.addAll(EnumSet.allOf(InternalField.class)); diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 7b0b9e6ec57..548086cc2d3 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -2081,9 +2081,10 @@ Select\ all\ changes\ on\ the\ right=Select all changes on the right Dismiss=Dismiss Mark\ all\ changes\ as\ accepted=Mark all changes as accepted Unmark\ all\ changes=Unmark all changes + Normalize\ newline\ characters=Normalize newline characters Normalizes\ all\ newline\ characters\ in\ the\ field\ content.=Normalizes all newline characters in the field content. -Index=Index + Independent=Independent Intersection=Intersection Union=Union @@ -2098,3 +2099,12 @@ Invalid\ regular\ expression.=Invalid regular expression. Keyword\ delimiter=Keyword delimiter Hierarchical\ keyword\ delimiter=Hierarchical keyword delimiter Escape\ ampersands=Escape ampersands + + +Add\ new\ Field=Add new Field +Add\ new\ entry\ type=Add new entry type +Field\ type=Field type +Required\ and\ optional\ fields=Required and optional fields +Index=Index +Remove\ entry\ type=Remove entry type +Remove\ field\ from\ entry\ type=Remove field from entry type