From 67e25a0c21ef3cdabd2d03541f4678694c5af6cf Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Mon, 4 Dec 2023 21:11:11 +0100 Subject: [PATCH] Add hide button for user comments (#10610) * Place BibTeX comment field first (and use JDK21 data structure) - Place all user comments togehter (and BibTeX standard comment first) - Switch from Set to StructuredSet * Enable "Add" button * Fix codestyle * Hide user comment button remove from grid todo move to prefs * Add new property for hiding/showing comment tabs move entry editor preferences * Add new property for hiding/showing comment tabs move entry editor preferences * wire checkbox/button to preferences * fix checkstyle * Move back * fix * fix test * Fix logic - and adapt tests --------- Co-authored-by: Christoph --- CHANGELOG.md | 1 + .../jabref/gui/entryeditor/CommentsTab.java | 96 +++++++++++++++---- .../gui/entryeditor/DeprecatedFieldsTab.java | 12 +-- .../entryeditor/EntryEditorPreferences.java | 17 +++- .../gui/entryeditor/FieldsEditorTab.java | 76 ++++++++------- .../entryeditor/OptionalFieldsTabBase.java | 10 +- .../gui/entryeditor/OtherFieldsTab.java | 12 +-- .../gui/entryeditor/RequiredFieldsTab.java | 6 +- .../gui/entryeditor/UserDefinedFieldsTab.java | 3 +- .../entryeditor/EntryEditorTab.fxml | 5 +- .../entryeditor/EntryEditorTab.java | 2 + .../entryeditor/EntryEditorTabViewModel.java | 8 ++ .../citationstyle/JabRefItemDataProvider.java | 11 ++- .../java/org/jabref/model/entry/BibEntry.java | 10 +- .../org/jabref/model/entry/BibEntryType.java | 19 ++-- .../entry/field/UserSpecificCommentField.java | 3 +- .../jabref/preferences/GuiPreferences.java | 8 -- .../jabref/preferences/JabRefPreferences.java | 11 ++- src/main/resources/l10n/JabRef_en.properties | 4 + .../gui/entryeditor/CommentsTabTest.java | 41 +++++++- 20 files changed, 248 insertions(+), 107 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc66aecf4e3..6d4d298bfb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We replaced "SearchAll" in Web Search by "Search Selected". [#10556](https://github.com/JabRef/jabref/issues/10556) - Short DOI formatter now checks, if the value is already formatted. If so, it returns the value instead of calling the ShortDOIService again. [#10589](https://github.com/JabRef/jabref/issues/10589) - We upgraded to JavaFX 21.0.1. As a consequence JabRef requires now macOS 11 or later and GTK 3.8 or later on Linux [10627](https://github.com/JabRef/jabref/pull/10627). +- A user-specific comment fields is not enabled by default, but can be enabled using the "Add" button. [#10424](https://github.com/JabRef/jabref/issues/10424) - We upgraded to Lucene 9.9 for the fulltext search. The search index will be rebuild. [#10686](https://github.com/JabRef/jabref/pull/10686) ### Fixed diff --git a/src/main/java/org/jabref/gui/entryeditor/CommentsTab.java b/src/main/java/org/jabref/gui/entryeditor/CommentsTab.java index 269ab381a83..dc4434ce5d4 100644 --- a/src/main/java/org/jabref/gui/entryeditor/CommentsTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/CommentsTab.java @@ -1,16 +1,25 @@ package org.jabref.gui.entryeditor; +import java.util.Comparator; import java.util.LinkedHashSet; import java.util.Map; -import java.util.Set; +import java.util.Optional; +import java.util.SequencedSet; import java.util.stream.Collectors; import javax.swing.undo.UndoManager; +import javafx.collections.ObservableList; +import javafx.geometry.VPos; +import javafx.scene.control.Button; +import javafx.scene.layout.Priority; +import javafx.scene.layout.RowConstraints; + import org.jabref.gui.DialogService; import org.jabref.gui.StateManager; import org.jabref.gui.autocompleter.SuggestionProviders; import org.jabref.gui.fieldeditors.FieldEditorFX; +import org.jabref.gui.fieldeditors.FieldNameLabel; import org.jabref.gui.icon.IconTheme; import org.jabref.gui.theme.ThemeManager; import org.jabref.gui.util.TaskExecutor; @@ -28,6 +37,10 @@ public class CommentsTab extends FieldsEditorTab { public static final String NAME = "Comments"; private final String defaultOwner; + private final UserSpecificCommentField userSpecificCommentField; + + private final EntryEditorPreferences entryEditorPreferences; + public CommentsTab(PreferencesService preferences, BibDatabaseContext databaseContext, SuggestionProviders suggestionProviders, @@ -54,38 +67,89 @@ public CommentsTab(PreferencesService preferences, this.defaultOwner = preferences.getOwnerPreferences().getDefaultOwner(); setText(Localization.lang("Comments")); setGraphic(IconTheme.JabRefIcons.COMMENT.getGraphicNode()); + + userSpecificCommentField = new UserSpecificCommentField(defaultOwner); + entryEditorPreferences = preferences.getEntryEditorPreferences(); } @Override - protected Set determineFieldsToShow(BibEntry entry) { - UserSpecificCommentField defaultCommentField = new UserSpecificCommentField(defaultOwner); + protected SequencedSet determineFieldsToShow(BibEntry entry) { + SequencedSet comments = new LinkedHashSet<>(); - // As default: Show BibTeX comment field and the user-specific comment field of the default owner - Set comments = new LinkedHashSet<>(Set.of(defaultCommentField, StandardField.COMMENT)); + // First comes the standard comment field + comments.add(StandardField.COMMENT); - comments.addAll(entry.getFields().stream() - .filter(field -> field instanceof UserSpecificCommentField || - field.getName().toLowerCase().contains("comment")) - .collect(Collectors.toSet())); + // Also show comment field of the current user (if enabled in the preferences) + if (entry.hasField(userSpecificCommentField) || entryEditorPreferences.shouldShowUserCommentsFields()) { + comments.add(userSpecificCommentField); + } + // Show all non-empty comment fields (otherwise, they are completely hidden) + comments.addAll(entry.getFields().stream() + .filter(field -> (field instanceof UserSpecificCommentField && !field.equals(userSpecificCommentField)) + || field.getName().toLowerCase().contains("comment")) + .sorted(Comparator.comparing(Field::getName)) + .collect(Collectors.toCollection(LinkedHashSet::new))); return comments; } + /** + * Comment editors: three times size of button + */ + private void setCompressedRowLayout() { + int numberOfComments = gridPane.getRowCount() - 1; + double totalWeight = numberOfComments * 3 + 1; + + RowConstraints commentConstraint = new RowConstraints(); + commentConstraint.setVgrow(Priority.ALWAYS); + commentConstraint.setValignment(VPos.TOP); + double commentHeightPercent = 3.0 / totalWeight * 100.0; + commentConstraint.setPercentHeight(commentHeightPercent); + + RowConstraints buttonConstraint = new RowConstraints(); + buttonConstraint.setVgrow(Priority.ALWAYS); + buttonConstraint.setValignment(VPos.TOP); + double addButtonHeightPercent = 1.0 / totalWeight * 100.0; + buttonConstraint.setPercentHeight(addButtonHeightPercent); + + ObservableList rowConstraints = gridPane.getRowConstraints(); + rowConstraints.clear(); + for (int i = 1; i <= numberOfComments; i++) { + rowConstraints.add(commentConstraint); + } + rowConstraints.add(buttonConstraint); + } + @Override protected void setupPanel(BibEntry entry, boolean compressed) { super.setupPanel(entry, compressed); + Optional fieldEditorForUserDefinedComment = editors.entrySet().stream().filter(f -> f.getKey().getName().contains(defaultOwner)).map(Map.Entry::getValue).findFirst(); for (Map.Entry fieldEditorEntry : editors.entrySet()) { Field field = fieldEditorEntry.getKey(); FieldEditorFX editor = fieldEditorEntry.getValue(); - if (field instanceof UserSpecificCommentField) { - if (field.getName().contains(defaultOwner)) { - editor.getNode().setDisable(false); - } - } else { - editor.getNode().setDisable(!field.getName().equals(StandardField.COMMENT.getName())); - } + boolean isStandardBibtexComment = field == StandardField.COMMENT; + boolean isDefaultOwnerComment = field.equals(userSpecificCommentField); + boolean shouldBeEnabled = isStandardBibtexComment || isDefaultOwnerComment; + editor.getNode().setDisable(!shouldBeEnabled); + } + + // Show "Hide" button only if user-specific comment field is empty. Otherwise, it is a strange UI, because the + // button would just disappear and no change **in the current** editor would be made + if (entryEditorPreferences.shouldShowUserCommentsFields() && !entry.hasField(userSpecificCommentField)) { + Button hideDefaultOwnerCommentButton = new Button(Localization.lang("Hide user comments")); + hideDefaultOwnerCommentButton.setOnAction(e -> { + var labelForField = gridPane.getChildren().stream().filter(s -> s instanceof FieldNameLabel).filter(x -> ((FieldNameLabel) x).getText().equals(userSpecificCommentField.getDisplayName())).findFirst(); + labelForField.ifPresent(label -> gridPane.getChildren().remove(label)); + fieldEditorForUserDefinedComment.ifPresent(f -> gridPane.getChildren().remove(f.getNode())); + editors.remove(userSpecificCommentField); + + entryEditorPreferences.setShowUserCommentsFields(false); + setupPanel(entry, false); + }); + gridPane.add(hideDefaultOwnerCommentButton, 1, gridPane.getRowCount(), 2, 1); + setCompressedRowLayout(); } } } diff --git a/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java b/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java index a40302d54c6..ca15fe49b7c 100644 --- a/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java @@ -1,8 +1,8 @@ package org.jabref.gui.entryeditor; -import java.util.Collections; +import java.util.LinkedHashSet; import java.util.Optional; -import java.util.Set; +import java.util.SequencedSet; import java.util.stream.Collectors; import javax.swing.undo.UndoManager; @@ -59,14 +59,14 @@ public DeprecatedFieldsTab(BibDatabaseContext databaseContext, } @Override - protected Set determineFieldsToShow(BibEntry entry) { + protected SequencedSet determineFieldsToShow(BibEntry entry) { BibDatabaseMode mode = databaseContext.getMode(); Optional entryType = entryTypesManager.enrich(entry.getType(), mode); if (entryType.isPresent()) { - return entryType.get().getDeprecatedFields(mode).stream().filter(field -> !entry.getField(field).isEmpty()).collect(Collectors.toSet()); + return entryType.get().getDeprecatedFields(mode).stream().filter(field -> !entry.getField(field).isEmpty()).collect(Collectors.toCollection(LinkedHashSet::new)); } else { - // Entry type unknown -> treat all fields as required - return Collections.emptySet(); + // Entry type unknown -> treat all fields as required (thus no optional fields) + return new LinkedHashSet<>(); } } } diff --git a/src/main/java/org/jabref/gui/entryeditor/EntryEditorPreferences.java b/src/main/java/org/jabref/gui/entryeditor/EntryEditorPreferences.java index c2afea3b0af..ae0fda0194c 100644 --- a/src/main/java/org/jabref/gui/entryeditor/EntryEditorPreferences.java +++ b/src/main/java/org/jabref/gui/entryeditor/EntryEditorPreferences.java @@ -48,6 +48,7 @@ public static JournalPopupEnabled fromString(String status) { private final BooleanProperty autoLinkFiles; private final ObjectProperty enablementStatus; private final BooleanProperty shouldShowSciteTab; + private final BooleanProperty showUserCommentsFields; public EntryEditorPreferences(Map> entryEditorTabList, Map> defaultEntryEditorTabList, @@ -60,7 +61,8 @@ public EntryEditorPreferences(Map> entryEditorTabList, double dividerPosition, boolean autolinkFilesEnabled, JournalPopupEnabled journalPopupEnabled, - boolean showSciteTab) { + boolean showSciteTab, + boolean showUserCommentsFields) { this.entryEditorTabList = new SimpleMapProperty<>(FXCollections.observableMap(entryEditorTabList)); this.defaultEntryEditorTabList = new SimpleMapProperty<>(FXCollections.observableMap(defaultEntryEditorTabList)); @@ -74,6 +76,7 @@ public EntryEditorPreferences(Map> entryEditorTabList, this.autoLinkFiles = new SimpleBooleanProperty(autolinkFilesEnabled); this.enablementStatus = new SimpleObjectProperty<>(journalPopupEnabled); this.shouldShowSciteTab = new SimpleBooleanProperty(showSciteTab); + this.showUserCommentsFields = new SimpleBooleanProperty(showUserCommentsFields); } public ObservableMap> getEntryEditorTabs() { @@ -211,4 +214,16 @@ public BooleanProperty shouldShowLSciteTabProperty() { public void setShouldShowSciteTab(boolean shouldShowSciteTab) { this.shouldShowSciteTab.set(shouldShowSciteTab); } + + public boolean shouldShowUserCommentsFields() { + return showUserCommentsFields.get(); + } + + public BooleanProperty showUserCommentsFieldsProperty() { + return showUserCommentsFields; + } + + public void setShowUserCommentsFields(boolean showUserCommentsFields) { + this.showUserCommentsFields.set(showUserCommentsFields); + } } diff --git a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java index 780af05c3c4..bdaf404b4c1 100644 --- a/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java +++ b/src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java @@ -6,11 +6,12 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Set; +import java.util.SequencedSet; import java.util.stream.Stream; import javax.swing.undo.UndoManager; +import javafx.collections.ObservableList; import javafx.geometry.VPos; import javafx.scene.Node; import javafx.scene.Parent; @@ -47,6 +48,7 @@ abstract class FieldsEditorTab extends EntryEditorTab { protected final BibDatabaseContext databaseContext; protected final Map editors = new LinkedHashMap<>(); + protected GridPane gridPane; private final boolean isCompressed; private final SuggestionProviders suggestionProviders; private final DialogService dialogService; @@ -59,7 +61,6 @@ abstract class FieldsEditorTab extends EntryEditorTab { private PreviewPanel previewPanel; private final UndoManager undoManager; private Collection fields = new ArrayList<>(); - private GridPane gridPane; public FieldsEditorTab(boolean compressed, BibDatabaseContext databaseContext, @@ -107,36 +108,22 @@ protected void setupPanel(BibEntry entry, boolean compressed) { fields = determineFieldsToShow(entry); - List