Skip to content
Draft
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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

Copy link
Member

Choose a reason for hiding this comment

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

Please fix. Take this as learning for git.

I see two options:

  1. git checkout main -- .idea/codeStyles/Project.xml
  2. Use the browser extension "refined github" and then you have the button "revert changes"

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

2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv

### Changed

- 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

### Removed
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)));
Copy link
Member

Choose a reason for hiding this comment

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

remove commented out code

}

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
Loading