Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tag-Based Selection for Entry fields in Preferences #12592

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Changes from all 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
20 changes: 11 additions & 9 deletions src/main/java/org/jabref/gui/preferences/entry/EntryTab.fxml
Original file line number Diff line number Diff line change
@@ -8,6 +8,8 @@
<?import javafx.scene.control.Tooltip?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import com.dlsc.gemsfx.TagsField?>

<fx:root spacing="10.0" type="VBox"
xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
fx:controller="org.jabref.gui.preferences.entry.EntryTab">
@@ -22,21 +24,21 @@

<CheckBox fx:id="resolveStrings" text="%Resolve BibTeX strings"/>
<HBox alignment="CENTER_LEFT" spacing="10.0">
<Label text="%Affected fields"/>
<TextField fx:id="resolveStringsForFields" HBox.hgrow="ALWAYS"
disable="${!resolveStrings.selected}">
<Label text="%Affected fields" minWidth="150" maxWidth="200"/>
<TagsField fx:id="resolveStringsForFields" HBox.hgrow="ALWAYS" disable="${!resolveStrings.selected}">
<HBox.margin>
<Insets top="-4.0"/>
</HBox.margin>
</TextField>
<padding>
<Insets left="20.0"/>
</padding>
</TagsField>
</HBox>

<HBox alignment="CENTER_LEFT" spacing="10.0">
<Label text="%Do not wrap when saving"/>
<TextField fx:id="nonWrappableFields" HBox.hgrow="ALWAYS"/>
<Label text="%Do not wrap when saving" minWidth="150" maxWidth="200"/>
<TagsField fx:id="nonWrappableFields" HBox.hgrow="ALWAYS">
<HBox.margin>
<Insets top="-4.0"/>
</HBox.margin>
</TagsField>
</HBox>

<Label styleClass="sectionHeader" text="%Entry owner"/>
63 changes: 56 additions & 7 deletions src/main/java/org/jabref/gui/preferences/entry/EntryTab.java
Original file line number Diff line number Diff line change
@@ -2,31 +2,39 @@

import java.util.function.UnaryOperator;

import javafx.css.PseudoClass;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;

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.preferences.AbstractPreferenceTabView;
import org.jabref.gui.preferences.PreferencesTab;
import org.jabref.gui.util.ViewModelListCellFactory;
import org.jabref.logic.help.HelpFile;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.entry.field.Field;

import com.airhacks.afterburner.views.ViewLoader;
import com.dlsc.gemsfx.TagsField;

public class EntryTab extends AbstractPreferenceTabView<EntryTabViewModel> implements PreferencesTab {


private static final PseudoClass FOCUSED = PseudoClass.getPseudoClass("focused");

@FXML private TextField keywordSeparator;

@FXML private CheckBox resolveStrings;
@FXML private TextField resolveStringsForFields;
@FXML private TextField nonWrappableFields;
@FXML private TagsField<Field> resolveStringsForFields;
@FXML private TagsField<Field> nonWrappableFields;

@FXML private CheckBox markOwner;
@FXML private TextField markOwnerName;
@@ -38,8 +46,8 @@ public class EntryTab extends AbstractPreferenceTabView<EntryTabViewModel> imple

public EntryTab() {
ViewLoader.view(this)
.root(this)
.load();
.root(this)
.load();
}

public void initialize() {
@@ -58,9 +66,10 @@ public void initialize() {

keywordSeparator.setTextFormatter(formatter);

setupTagsWraps();
setupTagsField();

resolveStrings.selectedProperty().bindBidirectional(viewModel.resolveStringsProperty());
resolveStringsForFields.textProperty().bindBidirectional(viewModel.resolveStringsForFieldsProperty());
nonWrappableFields.textProperty().bindBidirectional(viewModel.nonWrappableFieldsProperty());

markOwner.selectedProperty().bindBidirectional(viewModel.markOwnerProperty());
markOwnerName.textProperty().bindBidirectional(viewModel.markOwnerNameProperty());
@@ -75,6 +84,46 @@ public void initialize() {
actionFactory.configureIconButton(StandardActions.HELP, new HelpAction(HelpFile.OWNER, dialogService, preferences.getExternalApplicationsPreferences()), markOwnerHelp);
}

private void setupTagsField() {
resolveStringsForFields.setCellFactory(new ViewModelListCellFactory<Field>().withText(Field::getDisplayName));
resolveStringsForFields.tagsProperty().bindBidirectional(viewModel.resolveStringsForFieldsProperty());
resolveStringsForFields.setConverter(viewModel.getFieldStringConverter());
resolveStringsForFields.setTagViewFactory(this::createTag);
resolveStringsForFields.setShowSearchIcon(false);
resolveStringsForFields.setOnMouseClicked(event -> resolveStringsForFields.getEditor().requestFocus());
resolveStringsForFields.getEditor().getStyleClass().clear();
resolveStringsForFields.getEditor().focusedProperty().addListener((_, _, newValue) -> resolveStringsForFields.pseudoClassStateChanged(FOCUSED, newValue));
}

private void setupTagsWraps() {
nonWrappableFields.setCellFactory(new ViewModelListCellFactory<Field>().withText(Field::getDisplayName));
nonWrappableFields.tagsProperty().bindBidirectional(viewModel.nonWrappableFieldsProperty());
nonWrappableFields.setConverter(viewModel.getFieldStringConverter());
nonWrappableFields.setTagViewFactory(this::createWrapTag);
nonWrappableFields.setShowSearchIcon(false);
nonWrappableFields.setOnMouseClicked(event -> nonWrappableFields.getEditor().requestFocus());
nonWrappableFields.getEditor().getStyleClass().clear();
nonWrappableFields.getEditor().focusedProperty().addListener((_, _, newValue) -> nonWrappableFields.pseudoClassStateChanged(FOCUSED, newValue));
}

private Node createTag(Field field) {
Label tagLabel = new Label();
tagLabel.setText(field.getDisplayName());
tagLabel.setGraphic(IconTheme.JabRefIcons.REMOVE_TAGS.getGraphicNode());
tagLabel.getGraphic().setOnMouseClicked(event -> resolveStringsForFields.removeTags(field));
tagLabel.setContentDisplay(ContentDisplay.RIGHT);
return tagLabel;
}

private Node createWrapTag(Field field) {
Label tagLabel = new Label();
tagLabel.setText(field.getDisplayName());
tagLabel.setGraphic(IconTheme.JabRefIcons.REMOVE_TAGS.getGraphicNode());
tagLabel.getGraphic().setOnMouseClicked(event -> nonWrappableFields.removeTags(field));
tagLabel.setContentDisplay(ContentDisplay.RIGHT);
return tagLabel;
}

@Override
public String getTabName() {
return Localization.lang("Entry");
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
package org.jabref.gui.preferences.entry;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.util.StringConverter;

import org.jabref.gui.preferences.PreferenceTabViewModel;
import org.jabref.logic.bibtex.FieldPreferences;
import org.jabref.logic.preferences.CliPreferences;
import org.jabref.logic.preferences.OwnerPreferences;
import org.jabref.logic.preferences.TimestampPreferences;
import org.jabref.model.entry.BibEntryPreferences;
import org.jabref.model.entry.field.Field;
import org.jabref.model.entry.field.FieldFactory;

public class EntryTabViewModel implements PreferenceTabViewModel {

private final StringProperty keywordSeparatorProperty = new SimpleStringProperty("");

private final BooleanProperty resolveStringsProperty = new SimpleBooleanProperty();
private final StringProperty resolveStringsForFieldsProperty = new SimpleStringProperty("");
private final StringProperty nonWrappableFieldsProperty = new SimpleStringProperty("");
private final ListProperty<Field> resolveStringsForFieldsProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
private final ListProperty<Field> nonWrappableFieldsProperty = new SimpleListProperty<>(FXCollections.observableArrayList());

private final BooleanProperty markOwnerProperty = new SimpleBooleanProperty();
private final StringProperty markOwnerNameProperty = new SimpleStringProperty("");
@@ -42,10 +47,9 @@ public EntryTabViewModel(CliPreferences preferences) {
@Override
public void setValues() {
keywordSeparatorProperty.setValue(bibEntryPreferences.getKeywordSeparator().toString());

resolveStringsProperty.setValue(fieldPreferences.shouldResolveStrings());
resolveStringsForFieldsProperty.setValue(FieldFactory.serializeFieldsList(fieldPreferences.getResolvableFields()));
nonWrappableFieldsProperty.setValue(FieldFactory.serializeFieldsList(fieldPreferences.getNonWrappableFields()));
resolveStringsForFieldsProperty.setValue(FXCollections.observableArrayList(fieldPreferences.getResolvableFields()));
nonWrappableFieldsProperty.setValue(FXCollections.observableArrayList(fieldPreferences.getNonWrappableFields()));

markOwnerProperty.setValue(ownerPreferences.isUseOwner());
markOwnerNameProperty.setValue(ownerPreferences.getDefaultOwner());
@@ -58,17 +62,16 @@ public void setValues() {
@Override
public void storeSettings() {
bibEntryPreferences.keywordSeparatorProperty().setValue(keywordSeparatorProperty.getValue().charAt(0));

fieldPreferences.setResolveStrings(resolveStringsProperty.getValue());
fieldPreferences.setResolvableFields(FieldFactory.parseFieldList(resolveStringsForFieldsProperty.getValue().trim()));
fieldPreferences.setNonWrappableFields(FieldFactory.parseFieldList(nonWrappableFieldsProperty.getValue().trim()));

ownerPreferences.setUseOwner(markOwnerProperty.getValue());
ownerPreferences.setDefaultOwner(markOwnerNameProperty.getValue());
ownerPreferences.setOverwriteOwner(markOwnerOverwriteProperty.getValue());

timestampPreferences.setAddCreationDate(addCreationDateProperty.getValue());
timestampPreferences.setAddModificationDate(addModificationDateProperty.getValue());
fieldPreferences.getResolvableFields().clear();
fieldPreferences.getResolvableFields().addAll(resolveStringsForFieldsProperty.getValue());
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
fieldPreferences.getResolvableFields().addAll(resolveStringsForFieldsProperty.getValue());
fieldPreferences.getResolvableFields().addAll(resolveStringsForFieldsProperty.getValue().trim());

Copy link
Author

Choose a reason for hiding this comment

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

The values are in list format, so can not trim them here.

Copy link
Member

@subhramit subhramit Mar 13, 2025

Choose a reason for hiding this comment

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

Use map to trim them individually then.

resolveStringsForFieldsProperty.getValue().stream()
    .map(String::trim)
    .forEach(fieldPreferences.getResolvableFields()::add);

fieldPreferences.getNonWrappableFields().clear();
fieldPreferences.getNonWrappableFields().addAll(nonWrappableFieldsProperty.getValue());
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
fieldPreferences.getNonWrappableFields().addAll(nonWrappableFieldsProperty.getValue());
fieldPreferences.getNonWrappableFields().addAll(nonWrappableFieldsProperty.getValue().trim());

}

public StringProperty keywordSeparatorProperty() {
@@ -79,11 +82,11 @@ public BooleanProperty resolveStringsProperty() {
return resolveStringsProperty;
}

public StringProperty resolveStringsForFieldsProperty() {
public ListProperty<Field> resolveStringsForFieldsProperty() {
return resolveStringsForFieldsProperty;
}

public StringProperty nonWrappableFieldsProperty() {
public ListProperty<Field> nonWrappableFieldsProperty() {
return nonWrappableFieldsProperty;
}

@@ -110,4 +113,18 @@ public BooleanProperty addCreationDateProperty() {
public BooleanProperty addModificationDateProperty() {
return addModificationDateProperty;
}

public StringConverter<Field> getFieldStringConverter() {
return new StringConverter<>() {
@Override
public String toString(Field field) {
return field.getDisplayName();
}

@Override
public Field fromString(String string) {
return FieldFactory.parseField(string);
Copy link
Member

Choose a reason for hiding this comment

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

Why do we use parseField instead of parseFieldList now?

Copy link
Author

Choose a reason for hiding this comment

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

As I was updating it according to the autocompletion module, and that uses parseFieldList.

}
};
}
}
Loading