Skip to content

Commit

Permalink
Reimplement custom entry types dialog (#5799)
Browse files Browse the repository at this point in the history
  • Loading branch information
Siedlerchr committed Feb 19, 2020
1 parent c5209ba commit 58f1db5
Show file tree
Hide file tree
Showing 16 changed files with 634 additions and 392 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `#

- We cleaned up the group add/edit dialog. [#5826](https://github.com/JabRef/jabref/pull/5826)
- We reintroduced the index column. [#5844](https://github.com/JabRef/jabref/pull/5844)
- We reimplemented and improved the dialog "Customize entry types" [#4719](https://github.com/JabRef/jabref/issues/4719)
- Filenames of external files can no longer contain curly braces


Expand Down
1 change: 1 addition & 0 deletions src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,5 @@
requires org.antlr.antlr4.runtime;
requires flowless;
requires org.apache.tika.core;
requires javafx.base;
}
6 changes: 3 additions & 3 deletions src/main/java/org/jabref/gui/JabRefFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import org.jabref.gui.bibtexkeypattern.BibtexKeyPatternAction;
import org.jabref.gui.contentselector.ManageContentSelectorAction;
import org.jabref.gui.copyfiles.CopyFilesAction;
import org.jabref.gui.customentrytypes.CustomizeEntryAction;
import org.jabref.gui.customizefields.SetupGeneralFieldsAction;
import org.jabref.gui.dialogs.AutosaveUIManager;
import org.jabref.gui.documentviewer.ShowDocumentViewerAction;
Expand Down Expand Up @@ -829,9 +830,8 @@ private MenuBar createMenu() {

new SeparatorMenuItem(),

factory.createMenuItem(StandardActions.MANAGE_CONTENT_SELECTORS, new ManageContentSelectorAction(this, stateManager))
// TODO: Reenable customize entry types feature (https://github.com/JabRef/jabref/issues/4719)
//factory.createMenuItem(StandardActions.CUSTOMIZE_ENTRY_TYPES, new CustomizeEntryAction(this)),
factory.createMenuItem(StandardActions.MANAGE_CONTENT_SELECTORS, new ManageContentSelectorAction(this, stateManager)),
factory.createMenuItem(StandardActions.CUSTOMIZE_ENTRY_TYPES, new CustomizeEntryAction(stateManager, Globals.entryTypesManager))
);

help.getItems().addAll(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
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.function.Predicate;
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.logic.l10n.Localization;
import org.jabref.model.database.BibDatabaseMode;
import org.jabref.model.entry.BibEntryType;
import org.jabref.model.entry.BibEntryTypesManager;
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 de.saxsys.mvvmfx.utils.validation.FunctionBasedValidator;
import de.saxsys.mvvmfx.utils.validation.ValidationMessage;
import de.saxsys.mvvmfx.utils.validation.ValidationStatus;
import de.saxsys.mvvmfx.utils.validation.Validator;
import org.fxmisc.easybind.EasyBind;

public class CustomEntryTypeDialogViewModel {

public static final StringConverter<Field> 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 final ListProperty<BibEntryType> entryTypes;
private final ListProperty<Field> fields;
private final ObjectProperty<BibEntryType> selectedEntryTypes = new SimpleObjectProperty<>();
private final ListProperty<FieldViewModel> fieldsForType;
private final ObjectProperty<Field> selectedFieldToAdd = new SimpleObjectProperty<>();
private final StringProperty entryTypeToAdd = new SimpleStringProperty("");
private final ObservableList<BibEntryType> allEntryTypes;
private final ObservableList<FieldViewModel> allFieldsForType = FXCollections.observableArrayList(extractor -> new Observable[] {extractor.fieldName(), extractor.fieldType()});
private final ObjectProperty<Field> newFieldToAdd = new SimpleObjectProperty<>();
private final BibDatabaseMode mode;
private final Map<BibEntryType, List<FieldViewModel>> typesWithFields = new HashMap<>();
private final List<BibEntryType> typesToRemove = new ArrayList<>();

private final PreferencesService preferencesService;
private final BibEntryTypesManager entryTypesManager;

private final Validator entryTypeValidator;
private final Validator fieldValidator;

public CustomEntryTypeDialogViewModel(BibDatabaseMode mode, PreferencesService preferencesService, BibEntryTypesManager entryTypesManager) {
this.mode = mode;
this.preferencesService = preferencesService;
this.entryTypesManager = entryTypesManager;

Collection<BibEntryType> allTypes = entryTypesManager.getAllTypes(mode);
allTypes.addAll(entryTypesManager.getAllCustomTypes(mode));

allEntryTypes = FXCollections.observableArrayList(allTypes);
entryTypes = new SimpleListProperty<>(allEntryTypes);

fields = new SimpleListProperty<>(FXCollections.observableArrayList(FieldFactory.getCommonFields()));

for (BibEntryType entryType : allTypes) {
List<FieldViewModel> 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.fieldsForType = new SimpleListProperty<>(allFieldsForType);

EasyBind.subscribe(selectedEntryTypes, type -> {
if (type != null) {
allFieldsForType.setAll(typesWithFields.get(type));
}
});

Predicate<String> notEmpty = input -> (input != null) && !input.trim().isEmpty();
entryTypeValidator = new FunctionBasedValidator<>(entryTypeToAdd, notEmpty, ValidationMessage.error(Localization.lang("Entry type cannot be empty. Please enter a name.")));
fieldValidator = new FunctionBasedValidator<>(newFieldToAdd,
input -> input != null && !input.getDisplayName().isEmpty(),
ValidationMessage.error(Localization.lang("Field cannot be empty. Please enter a name.")));
}

public ListProperty<BibEntryType> entryTypes() {
return this.entryTypes;
}

public ListProperty<Field> fields() {
return this.fields;
}

public enum FieldType {

REQUIRED(Localization.lang("Required")),
OPTIONAL(Localization.lang("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 = newFieldToAdd.getValue();
FieldViewModel model = new FieldViewModel(field, true, FieldPriority.IMPORTANT, selectedEntryTypes.getValue());
typesWithFields.computeIfAbsent(selectedEntryTypes.getValue(), key -> new ArrayList<>()).add(model);
allFieldsForType.add(model);
newFieldToAddProperty().setValue(null);
}

public void addNewCustomEntryType() {
EntryType newentryType = new UnknownEntryType(entryTypeToAdd.getValue());
BibEntryType type = new BibEntryType(newentryType, new ArrayList<>(), Collections.emptyList());
this.allEntryTypes.add(type);
this.entryTypeToAdd.setValue("");
this.typesWithFields.put(type, new ArrayList<>());
}

public ObjectProperty<BibEntryType> selectedEntryTypeProperty() {
return this.selectedEntryTypes;
}

public ListProperty<FieldViewModel> fieldsforTypesProperty() {
return this.fieldsForType;
}

public ObjectProperty<Field> selectedFieldToAddProperty() {
return this.selectedFieldToAdd;
}

public StringProperty entryTypeToAddProperty() {
return this.entryTypeToAdd;
}

public ObjectProperty<Field> newFieldToAddProperty() {
return this.newFieldToAdd;
}

public ValidationStatus entryTypeValidationStatus() {
return entryTypeValidator.getValidationStatus();
}

public ValidationStatus fieldValidationStatus() {
return fieldValidator.getValidationStatus();
}

public void removeEntryType(BibEntryType focusedItem) {
typesToRemove.add(focusedItem);
typesWithFields.remove(focusedItem);
allEntryTypes.remove(focusedItem);
}

public void removeField(FieldViewModel focusedItem) {
typesWithFields.computeIfAbsent(selectedEntryTypes.getValue(), key -> new ArrayList<>()).remove(focusedItem);
allFieldsForType.remove(focusedItem);
}

public void apply() {

for (var typeWithField : typesWithFields.entrySet()) {
BibEntryType type = typeWithField.getKey();
List<FieldViewModel> allFields = typeWithField.getValue();

List<OrFields> requiredFields = allFields.stream().filter(field -> field.getFieldType() == FieldType.REQUIRED).map(FieldViewModel::getField).map(OrFields::new).collect(Collectors.toList());
List<BibField> 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);
entryTypesManager.addCustomOrModifiedType(newType, mode);
}

for (var type : typesToRemove) {
entryTypesManager.removeCustomOrModifiedEntryType(type, mode);
}
preferencesService.saveCustomEntryTypes();
//Reload types from preferences to make sure any modifications are present when reopening the dialog
entryTypesManager.addCustomOrModifiedTypes(preferencesService.loadBibEntryTypes(BibDatabaseMode.BIBTEX),
preferencesService.loadBibEntryTypes(BibDatabaseMode.BIBLATEX));
}

}
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
package org.jabref.gui.customentrytypes;

import org.jabref.gui.JabRefFrame;
import org.jabref.gui.StateManager;
import org.jabref.gui.actions.SimpleCommand;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntryTypesManager;

import static org.jabref.gui.actions.ActionHelper.needsDatabase;

public class CustomizeEntryAction extends SimpleCommand {

private final JabRefFrame frame;
private final StateManager stateManager;
private final BibEntryTypesManager entryTypesManager;

public CustomizeEntryAction(JabRefFrame frame) {
this.frame = frame;
public CustomizeEntryAction(StateManager stateManager, BibEntryTypesManager entryTypesManager) {
this.stateManager = stateManager;
this.executable.bind(needsDatabase(this.stateManager));
this.entryTypesManager = entryTypesManager;
}

@Override
public void execute() {
EntryTypeCustomizationDialog dialog = new EntryTypeCustomizationDialog();
BibDatabaseContext database = stateManager.getActiveDatabase().orElseThrow(() -> new NullPointerException("Database null"));
CustomizeEntryTypeDialogView dialog = new CustomizeEntryTypeDialogView(database, entryTypesManager);
dialog.showAndWait();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonType?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.DialogPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>

<?import org.jabref.gui.icon.JabRefIconView?>
<?import javafx.scene.control.Tooltip?>

<DialogPane prefHeight="596.0" prefWidth="680"
xmlns="http://javafx.com/javafx/8.0.171"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="org.jabref.gui.customentrytypes.CustomizeEntryTypeDialogView">
<content>
<HBox minHeight="-Infinity" minWidth="-Infinity"
prefHeight="400.0" prefWidth="400.0" spacing="10.0">
<children>
<VBox prefHeight="400.0" prefWidth="100.0" spacing="10.0">
<children>
<Label text="Entry types" />
<TableView fx:id="entryTypes" minWidth="-Infinity"
VBox.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="entryTypColumn" minWidth="100.0"
prefWidth="100.0" text="Entry Type" />
<TableColumn fx:id="entryTypeActionsColumn"
maxWidth="40.0" minWidth="40.0" resizable="false" />
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
<HBox spacing="10.0">
<children>
<TextField fx:id="addNewEntryType" />
<Button fx:id="addNewEntryTypeButton" prefHeight="20.0" prefWidth="20.0"
styleClass="icon-button,narrow" onAction="#addEntryType">
<graphic>
<JabRefIconView glyph="ADD_NOBOX" />
</graphic>
<tooltip>
<Tooltip text="%Add new entry type" />
</tooltip>
</Button>
</children>
<VBox.margin>
<Insets />
</VBox.margin>
</HBox>
</children>
</VBox>
<VBox prefHeight="400.0" prefWidth="100.0" spacing="10.0"
HBox.hgrow="ALWAYS">
<children>
<Label text="%Required and optional fields" />
<TableView fx:id="fields" minWidth="-Infinity"
VBox.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="fieldNameColumn"
minWidth="150.0" prefWidth="-1.0" text="%Field" />
<TableColumn fx:id="fieldTypeColumn"
minWidth="100.0" prefWidth="100.0" text="%Field type" />
<TableColumn fx:id="fieldTypeActionColumn"
maxWidth="40.0" minWidth="40.0" resizable="false" />
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
<HBox spacing="10.0">
<children>
<ComboBox fx:id="addNewField" editable="true"
prefWidth="150.0" />
<Button fx:id="addNewFieldButton" prefHeight="20.0" prefWidth="20.0"
styleClass="icon-button,narrow" onAction="#addNewField">
<graphic>
<JabRefIconView glyph="ADD_NOBOX" />
</graphic>
<tooltip>
<Tooltip text="%Add new Field" />
</tooltip>
</Button>
</children>
</HBox>
</children>
</VBox>
</children>
</HBox>
</content>
<ButtonType fx:id="applyButton" buttonData="OK_DONE"
text="%Apply" />
<ButtonType fx:constant="CANCEL" />
</DialogPane>
Loading

0 comments on commit 58f1db5

Please sign in to comment.