Skip to content
Open
2 changes: 1 addition & 1 deletion .idea/codeStyles/Project.xml
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No changes to project files

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. Sorry, I accidentally removed a line in a previous commit, and the latest one reintroduces it

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
- When the pin "Keep dialog always on top" in the global search dialog is selected, the search window stays open when double-clicking on an entry. [#13840](https://github.com/JabRef/jabref/issues/13840)
- We improved the UI of regex replacement in the citation key generator tab. [#13939](https://github.com/JabRef/jabref/pull/13939)
- We improved the way we check for matching curly braces in BibTeX fields and made error messages easier to understand. [#12605](https://github.com/JabRef/jabref/issues/12605)
- We replaced the standard ComboBox with a SearchableComboBox and added a free text field in custom Entry Types [#14082](https://github.com/JabRef/jabref/issues/14082)

### Fixed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import javafx.fxml.FXML;
import javafx.scene.Group;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
Expand Down Expand Up @@ -40,21 +39,24 @@
import com.tobiasdiez.easybind.EasyBind;
import de.saxsys.mvvmfx.utils.validation.visualization.ControlsFxVisualizer;
import jakarta.inject.Inject;
import org.controlsfx.control.SearchableComboBox;

public class CustomEntryTypesTab extends AbstractPreferenceTabView<CustomEntryTypesTabViewModel> implements PreferencesTab {

@FXML private TableView<EntryTypeViewModel> entryTypesTable;
@FXML private TableColumn<EntryTypeViewModel, String> entryTypColumn;
@FXML private TableColumn<EntryTypeViewModel, String> entryTypeActionsColumn;
@FXML private TextField addNewEntryType;
@FXML private TextField addNewCustomFieldText;
@FXML private TableView<FieldViewModel> fields;
@FXML private TableColumn<FieldViewModel, String> fieldNameColumn;
@FXML private TableColumn<FieldViewModel, Boolean> fieldTypeColumn;
@FXML private TableColumn<FieldViewModel, String> fieldTypeActionColumn;
@FXML private TableColumn<FieldViewModel, Boolean> fieldTypeMultilineColumn;
@FXML private ComboBox<Field> addNewField;
@FXML private SearchableComboBox<Field> addNewField;
@FXML private Button addNewEntryTypeButton;
@FXML private Button addNewFieldButton;
@FXML private Button addNewCustomFieldButton;

@Inject private StateManager stateManager;

Expand Down Expand Up @@ -89,9 +91,13 @@ public void initialize() {
addNewEntryTypeButton.disableProperty().bind(viewModel.entryTypeValidationStatus().validProperty().not());
addNewFieldButton.disableProperty().bind(viewModel.fieldValidationStatus().validProperty().not().or(viewModel.selectedEntryTypeProperty().isNull()));

viewModel.newCustomFieldToAddProperty().bindBidirectional(addNewCustomFieldText.textProperty());
addNewCustomFieldButton.disableProperty().bind(viewModel.customFieldValidationStatus().validProperty().not().or(viewModel.selectedEntryTypeProperty().isNull()));

Platform.runLater(() -> {
visualizer.initVisualization(viewModel.entryTypeValidationStatus(), addNewEntryType, true);
visualizer.initVisualization(viewModel.fieldValidationStatus(), addNewField, true);
visualizer.initVisualization(viewModel.customFieldValidationStatus(), addNewCustomFieldText, true);
});
}

Expand Down Expand Up @@ -203,7 +209,7 @@ private void setupFieldsTable() {
viewModel.newFieldToAddProperty().bindBidirectional(addNewField.valueProperty());
// The valueProperty() of addNewField ComboBox needs to be updated by typing text in the ComboBox textfield,
// since the enabled/disabled state of addNewFieldButton won't update otherwise
EasyBind.subscribe(addNewField.getEditor().textProperty(), text -> addNewField.setValue(FieldsUtil.FIELD_STRING_CONVERTER.fromString(text)));
// EasyBind.subscribe(addNewField.getEditor().textProperty(), text -> addNewField.setValue(FieldsUtil.FIELD_STRING_CONVERTER.fromString(text)));
}

private void makeRotatedColumnHeader(TableColumn<?, ?> column, String text) {
Expand Down Expand Up @@ -269,6 +275,11 @@ void addNewField() {
viewModel.addNewField();
}

@FXML
void addNewCustomField() {
viewModel.addNewCustomField();
}

@FXML
void resetEntryTypes() {
boolean reset = dialogService.showConfirmationDialogAndWait(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.jabref.model.entry.field.FieldProperty;
import org.jabref.model.entry.field.FieldTextMapper;
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;

Expand All @@ -45,6 +46,7 @@ public class CustomEntryTypesTabViewModel implements PreferenceTabViewModel {
private final ObjectProperty<EntryTypeViewModel> selectedEntryType = new SimpleObjectProperty<>();
private final StringProperty entryTypeToAdd = new SimpleStringProperty("");
private final ObjectProperty<Field> newFieldToAdd = new SimpleObjectProperty<>();
private final StringProperty newCustomFieldToAdd = new SimpleStringProperty("");
private final ObservableList<EntryTypeViewModel> entryTypesWithFields = FXCollections.observableArrayList(extractor -> new Observable[] {extractor.entryType(), extractor.fields()});
private final List<BibEntryType> entryTypesToDelete = new ArrayList<>();

Expand All @@ -55,6 +57,7 @@ public class CustomEntryTypesTabViewModel implements PreferenceTabViewModel {

private final Validator entryTypeValidator;
private final Validator fieldValidator;
private final Validator customFieldValidator;
private final Set<Field> multiLineFields = new HashSet<>();

Predicate<Field> isMultiline = field -> this.multiLineFields.contains(field) || field.getProperties().contains(FieldProperty.MULTILINE_TEXT);
Expand All @@ -78,6 +81,11 @@ public CustomEntryTypesTabViewModel(BibDatabaseMode mode,
newFieldToAdd,
input -> (input != null) && StringUtil.isNotBlank(FieldTextMapper.getDisplayName(input)),
ValidationMessage.error(Localization.lang("Field cannot be empty. Please enter a name.")));
customFieldValidator = new FunctionBasedValidator<>(
newCustomFieldToAdd,
input -> StringUtil.isNotBlank(input) && !input.contains(" "),
ValidationMessage.error(Localization.lang("Field cannot be empty and must not contain spaces."))
);
}

@Override
Expand Down Expand Up @@ -167,6 +175,26 @@ public void addNewField() {
newFieldToAddProperty().setValue(null);
}

public void addNewCustomField() {
String fieldName = newCustomFieldToAdd.get().trim();
Field newField = new UnknownField(fieldName);

boolean fieldExists = displayNameExists(FieldTextMapper.getDisplayName(newField));

if (fieldExists) {
dialogService.showWarningDialogAndWait(
Localization.lang("Duplicate fields"),
Localization.lang("Warning: You added field \"%0\" twice. Only one will be kept.", FieldTextMapper.getDisplayName(newField)));
} else {
this.selectedEntryType.getValue().addField(new FieldViewModel(
newField,
FieldViewModel.Mandatory.REQUIRED,
FieldPriority.IMPORTANT,
false));
}
newCustomFieldToAdd.set("");
}

public boolean displayNameExists(String displayName) {
ObservableList<FieldViewModel> entryFields = this.selectedEntryType.getValue().fields();
return entryFields.stream().anyMatch(fieldViewModel ->
Expand Down Expand Up @@ -202,11 +230,19 @@ public ObservableList<Field> fieldsForAdding() {
return this.fieldsForAdding;
}

public StringProperty newCustomFieldToAddProperty() {
return this.newCustomFieldToAdd;
}

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

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

public ValidationStatus customFieldValidationStatus() {
return customFieldValidator.getValidationStatus();
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
Expand All @@ -11,6 +10,7 @@
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.VBox?>
<?import org.jabref.gui.icon.JabRefIconView?>
<?import org.controlsfx.control.SearchableComboBox?>
<fx:root spacing="10.0" type="VBox"
xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
fx:controller="org.jabref.gui.preferences.customentrytypes.CustomEntryTypesTab">
Expand All @@ -30,7 +30,7 @@
</columnResizePolicy>
</TableView>
<HBox spacing="10.0">
<TextField fx:id="addNewEntryType"/>
<TextField fx:id="addNewEntryType" promptText="Type new entry type..."/>
<Button fx:id="addNewEntryTypeButton"
prefHeight="20.0" prefWidth="20.0"
styleClass="icon-button,narrow" onAction="#addEntryType">
Expand Down Expand Up @@ -60,18 +60,37 @@
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
</columnResizePolicy>
</TableView>
<HBox spacing="10.0" alignment="BASELINE_CENTER">
<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>
<HBox spacing="10.0" alignment="BASELINE_LEFT">
<VBox spacing="5.0">
<HBox spacing="10.0" alignment="BASELINE_CENTER">
<SearchableComboBox 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>
</HBox>

<HBox spacing="10.0" alignment="BASELINE_CENTER">
<TextField fx:id="addNewCustomFieldText" prefWidth="150.0" promptText="%Type new custom field..."/>
<Button fx:id="addNewCustomFieldButton"
prefHeight="20.0" prefWidth="20.0"
styleClass="icon-button,narrow" onAction="#addNewCustomField">
<graphic>
<JabRefIconView glyph="ADD_NOBOX"/>
</graphic>
<tooltip>
<Tooltip text="%Add new custom field"/>
</tooltip>
</Button>
</HBox>
</VBox>

<Region HBox.hgrow="ALWAYS" />
<Button text="%Reset to default" onAction="#resetEntryTypes">
<graphic>
Expand Down
4 changes: 4 additions & 0 deletions jablib/src/main/resources/l10n/JabRef_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2270,6 +2270,10 @@ Required=Required
Entry\ type\ cannot\ be\ empty\ and\ must\ not\ contain\ spaces.=Entry type cannot be empty and must not contain spaces.
Field\ cannot\ be\ empty.\ Please\ enter\ a\ name.=Field cannot be empty. Please enter a name.

Add\ new\ custom\ field=Add new custom field
Field\ cannot\ be\ empty\ and\ must\ not\ contain\ spaces.=Field cannot be empty and must not contain spaces.
Type\ new\ custom\ field...=Type new custom field...

Capitalize\ current\ word=Capitalize current word
Delete\ text=Delete text
Make\ current\ word\ lowercase=Make current word lowercase
Expand Down