Skip to content

Commit 67e25a0

Browse files
kopporSiedlerchr
andauthored
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 <siedlerkiller@gmail.com>
1 parent b08e78c commit 67e25a0

20 files changed

+248
-107
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
2626
- We replaced "SearchAll" in Web Search by "Search Selected". [#10556](https://github.com/JabRef/jabref/issues/10556)
2727
- 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)
2828
- 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).
29+
- 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)
2930
- We upgraded to Lucene 9.9 for the fulltext search. The search index will be rebuild. [#10686](https://github.com/JabRef/jabref/pull/10686)
3031

3132
### Fixed

src/main/java/org/jabref/gui/entryeditor/CommentsTab.java

+80-16
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
package org.jabref.gui.entryeditor;
22

3+
import java.util.Comparator;
34
import java.util.LinkedHashSet;
45
import java.util.Map;
5-
import java.util.Set;
6+
import java.util.Optional;
7+
import java.util.SequencedSet;
68
import java.util.stream.Collectors;
79

810
import javax.swing.undo.UndoManager;
911

12+
import javafx.collections.ObservableList;
13+
import javafx.geometry.VPos;
14+
import javafx.scene.control.Button;
15+
import javafx.scene.layout.Priority;
16+
import javafx.scene.layout.RowConstraints;
17+
1018
import org.jabref.gui.DialogService;
1119
import org.jabref.gui.StateManager;
1220
import org.jabref.gui.autocompleter.SuggestionProviders;
1321
import org.jabref.gui.fieldeditors.FieldEditorFX;
22+
import org.jabref.gui.fieldeditors.FieldNameLabel;
1423
import org.jabref.gui.icon.IconTheme;
1524
import org.jabref.gui.theme.ThemeManager;
1625
import org.jabref.gui.util.TaskExecutor;
@@ -28,6 +37,10 @@ public class CommentsTab extends FieldsEditorTab {
2837
public static final String NAME = "Comments";
2938

3039
private final String defaultOwner;
40+
private final UserSpecificCommentField userSpecificCommentField;
41+
42+
private final EntryEditorPreferences entryEditorPreferences;
43+
3144
public CommentsTab(PreferencesService preferences,
3245
BibDatabaseContext databaseContext,
3346
SuggestionProviders suggestionProviders,
@@ -54,38 +67,89 @@ public CommentsTab(PreferencesService preferences,
5467
this.defaultOwner = preferences.getOwnerPreferences().getDefaultOwner();
5568
setText(Localization.lang("Comments"));
5669
setGraphic(IconTheme.JabRefIcons.COMMENT.getGraphicNode());
70+
71+
userSpecificCommentField = new UserSpecificCommentField(defaultOwner);
72+
entryEditorPreferences = preferences.getEntryEditorPreferences();
5773
}
5874

5975
@Override
60-
protected Set<Field> determineFieldsToShow(BibEntry entry) {
61-
UserSpecificCommentField defaultCommentField = new UserSpecificCommentField(defaultOwner);
76+
protected SequencedSet<Field> determineFieldsToShow(BibEntry entry) {
77+
SequencedSet<Field> comments = new LinkedHashSet<>();
6278

63-
// As default: Show BibTeX comment field and the user-specific comment field of the default owner
64-
Set<Field> comments = new LinkedHashSet<>(Set.of(defaultCommentField, StandardField.COMMENT));
79+
// First comes the standard comment field
80+
comments.add(StandardField.COMMENT);
6581

66-
comments.addAll(entry.getFields().stream()
67-
.filter(field -> field instanceof UserSpecificCommentField ||
68-
field.getName().toLowerCase().contains("comment"))
69-
.collect(Collectors.toSet()));
82+
// Also show comment field of the current user (if enabled in the preferences)
83+
if (entry.hasField(userSpecificCommentField) || entryEditorPreferences.shouldShowUserCommentsFields()) {
84+
comments.add(userSpecificCommentField);
85+
}
7086

87+
// Show all non-empty comment fields (otherwise, they are completely hidden)
88+
comments.addAll(entry.getFields().stream()
89+
.filter(field -> (field instanceof UserSpecificCommentField && !field.equals(userSpecificCommentField))
90+
|| field.getName().toLowerCase().contains("comment"))
91+
.sorted(Comparator.comparing(Field::getName))
92+
.collect(Collectors.toCollection(LinkedHashSet::new)));
7193
return comments;
7294
}
7395

96+
/**
97+
* Comment editors: three times size of button
98+
*/
99+
private void setCompressedRowLayout() {
100+
int numberOfComments = gridPane.getRowCount() - 1;
101+
double totalWeight = numberOfComments * 3 + 1;
102+
103+
RowConstraints commentConstraint = new RowConstraints();
104+
commentConstraint.setVgrow(Priority.ALWAYS);
105+
commentConstraint.setValignment(VPos.TOP);
106+
double commentHeightPercent = 3.0 / totalWeight * 100.0;
107+
commentConstraint.setPercentHeight(commentHeightPercent);
108+
109+
RowConstraints buttonConstraint = new RowConstraints();
110+
buttonConstraint.setVgrow(Priority.ALWAYS);
111+
buttonConstraint.setValignment(VPos.TOP);
112+
double addButtonHeightPercent = 1.0 / totalWeight * 100.0;
113+
buttonConstraint.setPercentHeight(addButtonHeightPercent);
114+
115+
ObservableList<RowConstraints> rowConstraints = gridPane.getRowConstraints();
116+
rowConstraints.clear();
117+
for (int i = 1; i <= numberOfComments; i++) {
118+
rowConstraints.add(commentConstraint);
119+
}
120+
rowConstraints.add(buttonConstraint);
121+
}
122+
74123
@Override
75124
protected void setupPanel(BibEntry entry, boolean compressed) {
76125
super.setupPanel(entry, compressed);
77126

127+
Optional<FieldEditorFX> fieldEditorForUserDefinedComment = editors.entrySet().stream().filter(f -> f.getKey().getName().contains(defaultOwner)).map(Map.Entry::getValue).findFirst();
78128
for (Map.Entry<Field, FieldEditorFX> fieldEditorEntry : editors.entrySet()) {
79129
Field field = fieldEditorEntry.getKey();
80130
FieldEditorFX editor = fieldEditorEntry.getValue();
81131

82-
if (field instanceof UserSpecificCommentField) {
83-
if (field.getName().contains(defaultOwner)) {
84-
editor.getNode().setDisable(false);
85-
}
86-
} else {
87-
editor.getNode().setDisable(!field.getName().equals(StandardField.COMMENT.getName()));
88-
}
132+
boolean isStandardBibtexComment = field == StandardField.COMMENT;
133+
boolean isDefaultOwnerComment = field.equals(userSpecificCommentField);
134+
boolean shouldBeEnabled = isStandardBibtexComment || isDefaultOwnerComment;
135+
editor.getNode().setDisable(!shouldBeEnabled);
136+
}
137+
138+
// Show "Hide" button only if user-specific comment field is empty. Otherwise, it is a strange UI, because the
139+
// button would just disappear and no change **in the current** editor would be made
140+
if (entryEditorPreferences.shouldShowUserCommentsFields() && !entry.hasField(userSpecificCommentField)) {
141+
Button hideDefaultOwnerCommentButton = new Button(Localization.lang("Hide user comments"));
142+
hideDefaultOwnerCommentButton.setOnAction(e -> {
143+
var labelForField = gridPane.getChildren().stream().filter(s -> s instanceof FieldNameLabel).filter(x -> ((FieldNameLabel) x).getText().equals(userSpecificCommentField.getDisplayName())).findFirst();
144+
labelForField.ifPresent(label -> gridPane.getChildren().remove(label));
145+
fieldEditorForUserDefinedComment.ifPresent(f -> gridPane.getChildren().remove(f.getNode()));
146+
editors.remove(userSpecificCommentField);
147+
148+
entryEditorPreferences.setShowUserCommentsFields(false);
149+
setupPanel(entry, false);
150+
});
151+
gridPane.add(hideDefaultOwnerCommentButton, 1, gridPane.getRowCount(), 2, 1);
152+
setCompressedRowLayout();
89153
}
90154
}
91155
}

src/main/java/org/jabref/gui/entryeditor/DeprecatedFieldsTab.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package org.jabref.gui.entryeditor;
22

3-
import java.util.Collections;
3+
import java.util.LinkedHashSet;
44
import java.util.Optional;
5-
import java.util.Set;
5+
import java.util.SequencedSet;
66
import java.util.stream.Collectors;
77

88
import javax.swing.undo.UndoManager;
@@ -59,14 +59,14 @@ public DeprecatedFieldsTab(BibDatabaseContext databaseContext,
5959
}
6060

6161
@Override
62-
protected Set<Field> determineFieldsToShow(BibEntry entry) {
62+
protected SequencedSet<Field> determineFieldsToShow(BibEntry entry) {
6363
BibDatabaseMode mode = databaseContext.getMode();
6464
Optional<BibEntryType> entryType = entryTypesManager.enrich(entry.getType(), mode);
6565
if (entryType.isPresent()) {
66-
return entryType.get().getDeprecatedFields(mode).stream().filter(field -> !entry.getField(field).isEmpty()).collect(Collectors.toSet());
66+
return entryType.get().getDeprecatedFields(mode).stream().filter(field -> !entry.getField(field).isEmpty()).collect(Collectors.toCollection(LinkedHashSet::new));
6767
} else {
68-
// Entry type unknown -> treat all fields as required
69-
return Collections.emptySet();
68+
// Entry type unknown -> treat all fields as required (thus no optional fields)
69+
return new LinkedHashSet<>();
7070
}
7171
}
7272
}

src/main/java/org/jabref/gui/entryeditor/EntryEditorPreferences.java

+16-1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public static JournalPopupEnabled fromString(String status) {
4848
private final BooleanProperty autoLinkFiles;
4949
private final ObjectProperty<JournalPopupEnabled> enablementStatus;
5050
private final BooleanProperty shouldShowSciteTab;
51+
private final BooleanProperty showUserCommentsFields;
5152

5253
public EntryEditorPreferences(Map<String, Set<Field>> entryEditorTabList,
5354
Map<String, Set<Field>> defaultEntryEditorTabList,
@@ -60,7 +61,8 @@ public EntryEditorPreferences(Map<String, Set<Field>> entryEditorTabList,
6061
double dividerPosition,
6162
boolean autolinkFilesEnabled,
6263
JournalPopupEnabled journalPopupEnabled,
63-
boolean showSciteTab) {
64+
boolean showSciteTab,
65+
boolean showUserCommentsFields) {
6466

6567
this.entryEditorTabList = new SimpleMapProperty<>(FXCollections.observableMap(entryEditorTabList));
6668
this.defaultEntryEditorTabList = new SimpleMapProperty<>(FXCollections.observableMap(defaultEntryEditorTabList));
@@ -74,6 +76,7 @@ public EntryEditorPreferences(Map<String, Set<Field>> entryEditorTabList,
7476
this.autoLinkFiles = new SimpleBooleanProperty(autolinkFilesEnabled);
7577
this.enablementStatus = new SimpleObjectProperty<>(journalPopupEnabled);
7678
this.shouldShowSciteTab = new SimpleBooleanProperty(showSciteTab);
79+
this.showUserCommentsFields = new SimpleBooleanProperty(showUserCommentsFields);
7780
}
7881

7982
public ObservableMap<String, Set<Field>> getEntryEditorTabs() {
@@ -211,4 +214,16 @@ public BooleanProperty shouldShowLSciteTabProperty() {
211214
public void setShouldShowSciteTab(boolean shouldShowSciteTab) {
212215
this.shouldShowSciteTab.set(shouldShowSciteTab);
213216
}
217+
218+
public boolean shouldShowUserCommentsFields() {
219+
return showUserCommentsFields.get();
220+
}
221+
222+
public BooleanProperty showUserCommentsFieldsProperty() {
223+
return showUserCommentsFields;
224+
}
225+
226+
public void setShowUserCommentsFields(boolean showUserCommentsFields) {
227+
this.showUserCommentsFields.set(showUserCommentsFields);
228+
}
214229
}

src/main/java/org/jabref/gui/entryeditor/FieldsEditorTab.java

+41-35
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
import java.util.List;
77
import java.util.Map;
88
import java.util.Objects;
9-
import java.util.Set;
9+
import java.util.SequencedSet;
1010
import java.util.stream.Stream;
1111

1212
import javax.swing.undo.UndoManager;
1313

14+
import javafx.collections.ObservableList;
1415
import javafx.geometry.VPos;
1516
import javafx.scene.Node;
1617
import javafx.scene.Parent;
@@ -47,6 +48,7 @@
4748
abstract class FieldsEditorTab extends EntryEditorTab {
4849
protected final BibDatabaseContext databaseContext;
4950
protected final Map<Field, FieldEditorFX> editors = new LinkedHashMap<>();
51+
protected GridPane gridPane;
5052
private final boolean isCompressed;
5153
private final SuggestionProviders suggestionProviders;
5254
private final DialogService dialogService;
@@ -59,7 +61,6 @@ abstract class FieldsEditorTab extends EntryEditorTab {
5961
private PreviewPanel previewPanel;
6062
private final UndoManager undoManager;
6163
private Collection<Field> fields = new ArrayList<>();
62-
private GridPane gridPane;
6364

6465
public FieldsEditorTab(boolean compressed,
6566
BibDatabaseContext databaseContext,
@@ -107,36 +108,22 @@ protected void setupPanel(BibEntry entry, boolean compressed) {
107108

108109
fields = determineFieldsToShow(entry);
109110

110-
List<Label> labels = new ArrayList<>();
111-
for (Field field : fields) {
112-
FieldEditorFX fieldEditor = FieldEditors.getForField(
113-
field,
114-
taskExecutor,
115-
dialogService,
116-
journalAbbreviationRepository,
117-
preferences,
118-
databaseContext,
119-
entry.getType(),
120-
suggestionProviders,
121-
undoManager);
122-
fieldEditor.bindToEntry(entry);
123-
124-
editors.put(field, fieldEditor);
125-
labels.add(new FieldNameLabel(field));
126-
}
111+
List<Label> labels = fields
112+
.stream()
113+
.map(field -> createLabelAndEditor(entry, field))
114+
.toList();
127115

128116
ColumnConstraints columnExpand = new ColumnConstraints();
129117
columnExpand.setHgrow(Priority.ALWAYS);
130118

131119
ColumnConstraints columnDoNotContract = new ColumnConstraints();
132120
columnDoNotContract.setMinWidth(Region.USE_PREF_SIZE);
133-
int rows;
134121
if (compressed) {
135-
rows = (int) Math.ceil((double) fields.size() / 2);
122+
int rows = (int) Math.ceil((double) fields.size() / 2);
136123

137124
addColumn(gridPane, 0, labels.subList(0, rows));
138-
addColumn(gridPane, 3, labels.subList(rows, labels.size()));
139125
addColumn(gridPane, 1, editors.values().stream().map(FieldEditorFX::getNode).limit(rows));
126+
addColumn(gridPane, 3, labels.subList(rows, labels.size()));
140127
addColumn(gridPane, 4, editors.values().stream().map(FieldEditorFX::getNode).skip(rows));
141128

142129
columnExpand.setPercentWidth(40);
@@ -154,23 +141,39 @@ protected void setupPanel(BibEntry entry, boolean compressed) {
154141
}
155142
}
156143

144+
protected Label createLabelAndEditor(BibEntry entry, Field field) {
145+
FieldEditorFX fieldEditor = FieldEditors.getForField(
146+
field,
147+
taskExecutor,
148+
dialogService,
149+
journalAbbreviationRepository,
150+
preferences,
151+
databaseContext,
152+
entry.getType(),
153+
suggestionProviders,
154+
undoManager);
155+
fieldEditor.bindToEntry(entry);
156+
editors.put(field, fieldEditor);
157+
return new FieldNameLabel(field);
158+
}
159+
157160
private void setRegularRowLayout(GridPane gridPane) {
158161
double totalWeight = fields.stream()
159162
.mapToDouble(field -> editors.get(field).getWeight())
160163
.sum();
161-
162-
List<RowConstraints> constraints = new ArrayList<>();
163-
for (Field field : fields) {
164-
RowConstraints rowExpand = new RowConstraints();
165-
rowExpand.setVgrow(Priority.ALWAYS);
166-
rowExpand.setValignment(VPos.TOP);
167-
rowExpand.setPercentHeight(100 * editors.get(field).getWeight() / totalWeight);
168-
constraints.add(rowExpand);
169-
}
164+
List<RowConstraints> constraints = fields
165+
.stream()
166+
.map(field -> {
167+
RowConstraints rowExpand = new RowConstraints();
168+
rowExpand.setVgrow(Priority.ALWAYS);
169+
rowExpand.setValignment(VPos.TOP);
170+
rowExpand.setPercentHeight(100 * editors.get(field).getWeight() / totalWeight);
171+
return rowExpand;
172+
}).toList();
170173
gridPane.getRowConstraints().addAll(constraints);
171174
}
172175

173-
private void setCompressedRowLayout(GridPane gridPane, int rows) {
176+
protected static void setCompressedRowLayout(GridPane gridPane, int rows) {
174177
RowConstraints rowExpand = new RowConstraints();
175178
rowExpand.setVgrow(Priority.ALWAYS);
176179
rowExpand.setValignment(VPos.TOP);
@@ -179,8 +182,11 @@ private void setCompressedRowLayout(GridPane gridPane, int rows) {
179182
} else {
180183
rowExpand.setPercentHeight(100 / (double) rows);
181184
}
185+
186+
ObservableList<RowConstraints> rowConstraints = gridPane.getRowConstraints();
187+
rowConstraints.clear();
182188
for (int i = 0; i < rows; i++) {
183-
gridPane.getRowConstraints().add(rowExpand);
189+
rowConstraints.add(rowExpand);
184190
}
185191
}
186192

@@ -218,7 +224,7 @@ protected void previousPreviewStyle() {
218224
}
219225
}
220226

221-
protected abstract Set<Field> determineFieldsToShow(BibEntry entry);
227+
protected abstract SequencedSet<Field> determineFieldsToShow(BibEntry entry);
222228

223229
public Collection<Field> getShownFields() {
224230
return fields;
@@ -229,7 +235,7 @@ private void initPanel() {
229235
gridPane = new GridPane();
230236
gridPane.getStyleClass().add("editorPane");
231237

232-
// Warp everything in a scroll-pane
238+
// Wrap everything in a scroll-pane
233239
ScrollPane scrollPane = new ScrollPane();
234240
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
235241
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);

0 commit comments

Comments
 (0)